Beginner here… Is it a typical pattern to pass aro...
# coroutines
d
Beginner here… Is it a typical pattern to pass around the coroutine scope as a parameter to my suspen functions?
z
nope
o
that's a very big anti-pattern usually
👍 2
z
a suspend function`s scope is the scope that called the suspend function
o
and if you need a new scope object specifically, use
coroutineScope { ... }
👍 3
that enforces structured concurrency
👍 1
z
use
coroutineScope {  }
to access a nested scope under the coroutine scope that called the suspending function https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
pretty much the only time you pass around a
CoroutineScope
is when injecting it into a class’s constructor
very rarely as a function parameter.
d
then which
async
do I call? I suppose not the GlobalScope’s
z
you create an instance of
CoroutineScope
and store it somewhere. For example on Android I would create an instance of
CoroutineScope(Dispatchers.Default)
and store it in the
Application
subclass
d
oh, I see. I use Ktor, so I do have an application object that also serves as the coroutine scope
z
and inject it into classes that are scoped to the
Application
as well
I haven’t used
ktor
, but is the
async
you are trying to use scoped to an API call? or globally to the JVM?
I’m not sure what the Ktor equivalent to Android’s
Application
would be
if it’s an API call, I imagine you have some type of function to handle that request like:
Copy code
suspend fun handleRequest(request: Request): Response
So you could build an object graph for any dependencies related to that call:
Copy code
suspend fun handleRequest(request: Request): Response = coroutineScope {
  val scope : CoroutineScope = this@coroutineScope
  val graph = SomeGraphForThisApiCall(scope, otherDependencies)
}
d
that seems like a little too much boilerplate just to be able to run code asynchronously - is there a simpler way? how bad is it to do
GlobalScope.async
?
z
if code quality/correctness does not matter to you, then you can use
GlobalScope
o
it is honestly not that much boiler plate -- all you really need to do is say
coroutineScope { ... }
👍 1
z
however if this is within the scope of an API call, an error thrown by
GlobalScope.async { }
will not be thrown from where you call it, unless you use
.await()
on the deferred that’s returned
👍 1
@dorf you can do:
Copy code
suspend fun handleRequest(request: Request): Response = coroutineScope {
  async {
    ...
  }
}
o
an important thing to note is that I would expect that with ktor, there is almost no need to use
async
if you make an I/O call (or other blocking work), use
withContext(<http://Dispatchers.IO|Dispatchers.IO>) { ... }
, if you suspend ktor will probably switch to handling other requests automatically, as that's what coroutines are meant to do
👍 2
the only thing
async
is good for is parallel processing of work
👍 1
d
so does this makes sense?
Copy code
private suspend fun searchAllEngines(input: String) = coroutineScope {
        searchEngines.map {
            async<Collection<Result>> {
                try {
                    it.search(input)
                } catch (exc: Exception) {
                    logger.error("${it::class.simpleName} failed: ${exc.message}")
                    emptyList()
                }
            }
        }
    }
o
sure, as long as
it.search
is also
suspend
and appropriately switches to the IO dispatcher
d
cool 👍 thanks!