Patrick Ramsey
03/26/2021, 12:39 AMBut what if the extracted function contains a coroutine builder which is invoked on the current scope? In this case, theParticularly, what does it mean that having an explicit CoroutineScope as a field on a class containing the target function is preferable to calling CoroutineScope(…)? How else would you create an explicit CoroutineScope as a field, other thanmodifier on the extracted function is not enough. Makingsuspend
an extension method ondoWorld
is one of the solutions, but it may not always be applicable as it does not make the API clearer. The idiomatic solution is to have either an explicitCoroutineScope
as a field in a class containing the target function or an implicit one when the outer class implementsCoroutineScope
. As a last resort, CoroutineScope(coroutineContext) can be used, but such an approach is structurally unsafe because you no longer have control on the scope of execution of this method. Only private APIs can use this builder.CoroutineScope
val scope = CoroutineScope(…)
?Patrick Ramsey
03/26/2021, 12:51 AMPatrick Ramsey
03/26/2021, 1:43 AMPatrick Ramsey
03/26/2021, 1:43 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 1:44 AMupon further thought, is what that last sentence is actually saying that calling CoroutineScope(…).launch() is a bad idea? (as opposed to instantiating CoroutineScope as a member variable on a class with a defined lifecycle, then calling this.scope.launch() )Yea, i think you got it. Docs could definitely be more clear about that
Patrick Ramsey
03/26/2021, 1:44 AMPatrick Ramsey
03/26/2021, 1:51 AMcoroutineScope { ... }
When does that scope get cleaned up/joined? When control leaves the block?Patrick Ramsey
03/26/2021, 1:54 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 1:56 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 1:56 AMcoroutineScope {
launch {
suspendCoroutine {}
}
}
The coroutineScope
function will suspend foreverPatrick Ramsey
03/26/2021, 1:56 AMPatrick Ramsey
03/26/2021, 2:00 AMcoroutineScope {
fun onSomeEvent(<params>) {
launch { work }
}
someExternalSynchronousApiThatTakesACallback(onSomeEvent)
}
Patrick Ramsey
03/26/2021, 2:01 AMPatrick Ramsey
03/26/2021, 2:03 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 2:05 AMval job = Job().complete()
CoroutineScope(job).launch {}
Patrick Ramsey
03/26/2021, 2:06 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 2:06 AMPatrick Ramsey
03/26/2021, 2:06 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 2:07 AMPatrick Ramsey
03/26/2021, 2:07 AMPatrick Ramsey
03/26/2021, 2:07 AMPatrick Ramsey
03/26/2021, 2:09 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 2:17 AMCoroutineScope(Dispatchers.Default)
is much more clear than GlobalScope
imo. It’s not clear that the author necessarily intended Dispatchers.Default
to be used when using GlobalScope
, or if they were even aware that’s what it would mean.
4. If you’re writing an android app, you might eventually find it useful to be able to explicitly cancel everything that’s considered “scoped to the lifetime of the app” in UI tests.Patrick Ramsey
03/26/2021, 2:18 AMPatrick Ramsey
03/26/2021, 2:18 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 2:27 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 2:27 AMGlobalScope
, happy to rant about it any day 😛Patrick Ramsey
03/26/2021, 2:28 AMPatrick Ramsey
03/26/2021, 2:28 AMPatrick Ramsey
03/26/2021, 2:28 AMPatrick Ramsey
03/26/2021, 2:29 AMZach Klippenstein (he/him) [MOD]
03/26/2021, 2:38 AM