KamilH
06/21/2023, 4:03 PMwithContext
return a value when the job is canceled? In the Android application, when I call the following code:
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
val job = scope.launch {
val result = withErrorHandlingOnDispatcher(<http://Dispatchers.IO|Dispatchers.IO>) {
delay(2000)
}
println("*** result: $result")
}
scope.launch {
delay(500)
job.cancel()
}
suspend fun <T> withErrorHandlingOnDispatcher(
dispatcher: CoroutineDispatcher,
doWork: suspend () -> T,
): Result<T> = withContext(dispatcher) {
try {
Result.success(doWork())
} catch (exception: Exception) {
Result.failure(exception)
}.apply {
println("*** withErrorHandlingOnDispatcher result: $this")
}
}
I get the log:
withErrorHandlingOnDispatcher result: Failure(kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@5f15aa2)
which means that JobCancellationException
is being caught, but result: $result
is not printed. When I remove withContext(dispatcher) { }
it prints:
withErrorHandlingOnDispatcher result: Failure(kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@9a6574d)
result: Failure(kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@9a6574d)
I guess it’s expected behavior, but why does it work like that? Is there a way to return value from withContext
anyway?franztesca
06/21/2023, 4:50 PMCancellationException
.In this case withContext
suspends and resumes the caller coroutine and thus, the original coroutine will not be resumed with the result of withContext, but with CancellationException, which will terminate the coroutine without printing any result. If you want your code to run even when the coroutine is cancelled, then you should wrap everything in a withContext(NonCancellable)
, making the whole block non-cancellable (even though this is generally a rare requirement) or use can use try-finally
to make sure that the code in the finally
block is executed even on a cancelled coroutine.Joffrey
06/21/2023, 5:21 PMtry/catch
KamilH
06/21/2023, 6:01 PMwithErrorHandlingOnDispatcher
that returns a custom Result
type. A custom exception-handling logic is implemented in the catch
block according to the business requirements.
Currently, I’m implementing an upload feature where the user can cancel this process by clicking a button. I keep track of the “upload jobs”, and when the user clicks a button, I job.cancel()
(Retrofit
automatically cancels an HTTP call, so the requirement is met). I expected I would get a JobCancellationException
at the place where I do a call so I would be able to handle it properly (display an error and retry button). However, it seems like my assumption was wrong.Pablichjenkov
06/22/2023, 1:19 AMKamilH
06/22/2023, 4:50 AMdoWork
block is handled correctly, so CancellationException
is also