Kevin Gorham
12/11/2019, 1:14 PMoutput
but the second, similar, example does? No permutation of yield, delay or sleep seems to get output
to print in Example 1.
Example 1 (always prints start stop
):
@Test
fun testUnexpectedBehavior() = runBlocking {
print("start")
captureScope()
scope.launch {
print(" output")
}
print(" stop")
}
lateinit var scope: CoroutineScope
suspend fun captureScope() = coroutineScope {
scope = this
}
Example 2 (always prints `start output stop`:
@Test
fun testExpectedBehavior() = runBlocking {
print("start")
coroutineScope {
launch {
print(" output")
}
}
print(" stop")
}
tseisel
12/11/2019, 2:07 PMcoroutineScope
function suspends the caller until all children coroutine have terminated.
This means that in Example 2, "output" will always be printed before "stop".
In Example 1, you are capturing the CoroutineScope
from the coroutineScope
block. Because that block has run to completion, its scope is finished and you cannot launch coroutines in it.Kevin Gorham
12/11/2019, 2:26 PMits scope is finished and you cannot launch coroutines in itInteresting. This is the part that's surprising. It feels like in this case
scope.launch
should throw an exception. In a similar way that a cancelled job or channel would behave.
I added some logs and confirmed your explanation at the time of `scope.launch`:
• the job is not cancelled
• but the job is not active
• and the scope is not active
So I think I had a gap in my understanding on how isActive
behaves. I've only used it for cooperative cancellation but I've never observed how it behaves in this scenario. Thanks for the help! I'll look for some documentation on isActive
.bdawg.io
12/11/2019, 9:51 PMoutput
should never be printed because the captureScope
returns with scope
being setting to a cancelled CoroutineScope
because you're not launching inside of the coroutineScope
lambda. There's no benefit to capturing the CoroutineScope
because you already have one inside your runBlocking
(suspending) lambda