I’ve started a spike with Kotlin coroutines and am quite loving it so far, a lot of things that we’re doing now with tools like CompletableFuture or RxJava are becoming a lot simpler. However, I’m trying to understand what’s seen as best practice when it comes to exposing non blocking library functions.
Say I have an interface that represents a client for making non blocking network calls. I can see a number of ways of modelling this:
Modelling the call as a normal function returning a Deferred
fun makeCall(): Deferred<Response>
This makes it clear that the operation is non blocking, but presumably now my implementation will need to use its own scope/dispatcher - either hard coded or injected into the implementation - and thus the deferred would not be automatically cancelled if the parent task was.
Modelling the call as a suspend function
suspend fun makeCall(): Response
This makes it clear that the method should be called from another suspend function (so will have an implicit scope), but in this model, how does one launch child tasks? https://youtrack.jetbrains.com/issue/KT-17609 is talking about giving access to the current scope which would be used to launch other tasks, but it’s not clear how one is meant to do this. Is the right approach to essentially always use withContext() inside the implementation? Or does it make sense for the implementation to be a CoroutineScope of its own?
Alternatively, one could model it as an extension method on a scope
suspend fun CoroutineScope.makeCall(): Response
The implementation will now have implicit access to a scope so can use launch etc, although actually using this would be “interesting”.
Is there a resource describing best practices for this type of thing?