Recently, I've upgraded Kotlin Coroutines from experimental to 1.1.1 and faced the problem that job.cancel()
in new version works differently.
Here's the code with Experimental Coroutines:
fun <R : Any, T : Deferred<R>> T.runAsync(
job: Job,
onSuccess: (result: R) -> Unit,
onFailed: (errorMsg: String?) -> Unit) {
launch(UI, parent = job) {
try {
val result = [email protected]()
onSuccess(result)
} catch (e: Exception) {
onFailed(e.message)
}
}
}
Here's with 1.1.1:
fun <R : Any, T : Deferred<R>> T.runAsync(
job: Job,
onSuccess: (result: R) -> Unit,
onFailed: (errorMsg: String?) -> Unit) {
GlobalScope.launch(Dispatchers.Main + job) {
try {
val result = withContext(Dispatchers.IO) {
[email protected]()
}
onSuccess(result)
} catch (e: Exception) {
onFailed(e.message)
}
}
}
For example:
My fragment destroyed and called job.cancel()
during coroutine is running.
In experimental coroutines neither onSuccess()
nor onFailed()
will be called.
In 1.1.1: onFailed()
called because caught of JobCancellationException
I figured out to add catch (e: JobCancellationException)
, but it's impossible:
/**
* @suppress **This an internal API and should not be used from general code.**
*/
internal expect class JobCancellationException(
So, the question is: How to handle/ignore JobCancellationException
?
Once job.cancel is called, our coroutine moves to Cancelling state. But then, we see that Hello 3 and Hello 4 are printed to the terminal. Only after the work is done, the coroutine moves to Cancelled state. The coroutine work doesn’t just stop when cancel is called.
Use the CoroutineScopes defined in Jetpack: viewModelScope or lifecycleScope that cancels their work when their scope completes. If you’re creating your own CoroutineScope, make sure you’re tying it to a job and calling cancel when needed.
Here’s why we get the exception: the role of await is to suspend the coroutine until the result is computed; since the coroutine is cancelled, the result cannot be computed. Therefore, calling await after cancel leads to JobCancellationException: Job was cancelled.
The TimeoutCancellationException that is thrown by withTimeout is a subclass of CancellationException. We have not seen its stack trace printed on the console before. That is because inside a cancelled coroutine CancellationException is considered to be a normal reason for coroutine completion.
You try to catch the super-class CancellationException
instead, which is a part of the public API.
Do note that if something throws CancellationException
you are generally expected to rethrow it so upstream objects are notified about the cancellation. See Cancellation is Cooperative
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With