https://kotlinlang.org logo
Title
d

dorf

07/20/2020, 11:32 PM
Beginner here… Is it a typical pattern to pass around the coroutine scope as a parameter to my suspen functions?
z

zak.taccardi

07/20/2020, 11:32 PM
nope
o

octylFractal

07/20/2020, 11:33 PM
that's a very big anti-pattern usually
👍 2
z

zak.taccardi

07/20/2020, 11:33 PM
a suspend function`s scope is the scope that called the suspend function
o

octylFractal

07/20/2020, 11:34 PM
and if you need a new scope object specifically, use
coroutineScope { ... }
👍 3
that enforces structured concurrency
👍 1
z

zak.taccardi

07/20/2020, 11:34 PM
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

dorf

07/20/2020, 11:35 PM
then which
async
do I call? I suppose not the GlobalScope’s
z

zak.taccardi

07/20/2020, 11:37 PM
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

dorf

07/20/2020, 11:38 PM
oh, I see. I use Ktor, so I do have an application object that also serves as the coroutine scope
z

zak.taccardi

07/20/2020, 11:38 PM
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:
suspend fun handleRequest(request: Request): Response
So you could build an object graph for any dependencies related to that call:
suspend fun handleRequest(request: Request): Response = coroutineScope {
  val scope : CoroutineScope = this@coroutineScope
  val graph = SomeGraphForThisApiCall(scope, otherDependencies)
}
d

dorf

07/20/2020, 11:53 PM
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

zak.taccardi

07/20/2020, 11:53 PM
if code quality/correctness does not matter to you, then you can use
GlobalScope
o

octylFractal

07/20/2020, 11:54 PM
it is honestly not that much boiler plate -- all you really need to do is say
coroutineScope { ... }
👍 1
z

zak.taccardi

07/20/2020, 11:55 PM
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:
suspend fun handleRequest(request: Request): Response = coroutineScope {
  async {
    ...
  }
}
o

octylFractal

07/20/2020, 11:58 PM
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

dorf

07/21/2020, 12:26 AM
so does this makes sense?
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

octylFractal

07/21/2020, 12:34 AM
sure, as long as
it.search
is also
suspend
and appropriately switches to the IO dispatcher
d

dorf

07/21/2020, 12:39 AM
cool 👍 thanks!