Sebouh Aguehian
10/11/2019, 1:21 PMfun main() {
val job = GlobalScope.launch(Dispatchers.Main) {
println("This is executed before toBeCancelled")
toBeCancelled()
println("This is executed after toBeCancelled")
}
}
suspend fun toBeCancelled() {
withContext(EmptyCoroutineContext) {
coroutineContext[Job]!!.cancel()
println("This is executed before the delay")
delay(2000)
println("This is executed after the delay")
}
}
This is executed before toBeCancelled
This is executed before the delay
How come “This is executed after toBeCancelled” is not printed? My understanding is that withContext
would create a child job, and calling coroutineContext[Job]!!.cancel()
should only cancel that child job. How come it is impacting the parent?streetsofboston
10/11/2019, 1:32 PMwithContext { ... }
throws a CancellationException
. This bubbles up to the caller.suspend fun toBeCancelled() {
try {
withContext(EmptyCoroutineContext) {
coroutineContext[Job]!!.cancel()
println("This is executed before the delay")
delay(2000)
println("This is executed after the delay")
}
} catch (e: Throwable) { println("withContext threw exception $e"); throw e }
}
throw e
from the catch
clause will eat that cancellation exception and “This is executed after toBeCancelled” will be printed out (note: Don’t eat/forget-to-rethrow CancellationExceptions. It can break the Structured Concurrency!)Sebouh Aguehian
10/11/2019, 1:36 PMwithContext
? The following doesn’t cancel the parent.
fun main() {
val job = GlobalScope.launch(Dispatchers.Main) {
println("This is executed before toBeCancelled")
launch {
coroutineContext[Job]!!.cancel()
println("This is executed before the delay")
delay(2000)
println("This is executed after the delay")
}.join()
println("This is executed after toBeCancelled")
}
}
streetsofboston
10/11/2019, 1:37 PMlaunch { ... }.join()
is not like withContex
withContext
is more like async { .... }.await()
. It returns the value of its lambda.Sebouh Aguehian
10/11/2019, 1:38 PMstreetsofboston
10/11/2019, 1:40 PMlaunch
will never throw an exception. A call to join
won’t either. A launch
will cause the CoroutineScope’s CoroutineExceptionHandler to be called.
a call to async
will never throw an exception either, but a call to await
will throw an exception (if there was one thrown by the lambda of async
)withContext
, like async { ...}.await()
, re-throws any exception thrown in its lambda. And that exception can be a CancellationException (caused by a call to cancel
somewhere)Sebouh Aguehian
10/11/2019, 1:44 PMlaunch
, that bubble up right?streetsofboston
10/11/2019, 1:47 PMlaunch
will be handled by the CoroutineScope’s CoroutineExceptionHandler (CEH).
If that exception is a CancellationException, nothing really will happen, except for the CoroutineScope getting cancelled (unless a SupervisorScope is used). If it is not a CancelationException, depending whether you have a CEH installed or not and the implementation of your CEH and the implementation of your app’s ‘uncaught-exception-handler’, your app may crash.Sebouh Aguehian
10/11/2019, 1:53 PMfun main() {
val job = GlobalScope.launch(Dispatchers.Main) {
println("This is executed before toBeCancelled")
try {
launch {
throw Exception("shiiiii")
}.join()
} catch (e: Exception) {
println (e)
}
println("This is executed after toBeCancelled")
}
}
I’m getting:
This is executed before toBeCancelled
JobCancellationException: StandaloneCoroutine is cancelling; caused by Exception: shiiiii; job=StandaloneCoroutine{Cancelling}@11
This is executed after toBeCancelled
Exception: shiiiii
Seems like I’m able to catch it outside the launch?Pablichjenkov
10/11/2019, 2:07 PMcoroutineContext[Job]!!.cancel()
inside withContext
will return the parents job. withContext
switches Context but I don't think it creates a new child Job().Sebouh Aguehian
10/11/2019, 2:08 PMPablichjenkov
10/11/2019, 2:08 PM