

Coroutines and exceptions: things to know
source link: https://www.rockandnull.com/coroutines-and-exceptions/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Coroutines are an awesome way to write async code. Your code looks almost similar to the sync equivalent but it's not blocking.
When everything goes according to plan, no need to worry about weird behavior from coroutines. What happens though when things don't go according to plan, i.e. exceptions are thrown? Can those exceptions be caught like regular sync code?
The answer depends on the coroutine builder used.
async
The async
coroutine builder works as you would expect, i.e. similar to how sync code handles exceptions. They rely on the user to handle the exception, otherwise, it's thrown as unhandled.
val deferred = GlobalScope.async { throw ArithmeticException() } try { deferred.await() } catch (e: ArithmeticException) { // Exception caught }
launch
On the other hand, launch
coroutine builder treats exceptions as unhandled. These can be caught by Java's Thread.uncaughtExceptionHandler
.
try { GlobalScope.launch { throw ArithmeticException() } } catch (e: ArithmeticException) { // Exception will *not* be caught }
Of course, relying on Thread.uncaughtExceptionHandler
is not a great idea to handle all exceptions across your app.
Coroutine builders accept an additional CoroutineExceptionHandler
parameter for these cases.
val handler = CoroutineExceptionHandler { _, exception -> // Exception caught } GlobalScope.launch(handler) { throw ArithmeticException() }
Child-parent exception relationship
Speaking of exceptions, we should note the default behavior of coroutines when they encounter an exception.
A child coroutine encountering an exception will cancel itself and its parent coroutine with that exception.
val handler = CoroutineExceptionHandler { _, exception -> // ArithmeticException will be caught. // // 1st child will not complete because parent is cancelled (i.e. and // children as well) } GlobalScope.launch(handler) { launch { // 1st child delay(Long.MAX_VALUE) print("This will not be printed") } launch { // 2nd child throw ArithmeticException() } }
A small detail, but the original exception will be handled by the parent after
all its children coroutines terminate. So, if the 2nd child was running with
NonCancellable
context, the handler would be called after the completion of the 2nd child.
val handler = CoroutineExceptionHandler { _, exception -> // 1st child complete, then ArithmeticException will be caught. } GlobalScope.launch(handler) { launch(NonCancellable) { // 1st child delay(Long.MAX_VALUE) print("This will be printed") } launch { // 2nd child throw ArithmeticException() } }
A special exception
The CancellationException
is treated differently than the rest of the exceptions. It's ignored by the exception handlers and does not
cause the cancellation of the parent coroutine.
launch { val child = launch { delay(Long.MAX_VALUE) } child.join() child.cancel() // Parent is not cancelled, // although child is throwing a CancellationExeption print("Hello from parent") }
Multiple exceptions
In case multiple children throw exceptions, the first one wins. There is a way to catch additional exceptions that might have been thrown after the first one.
val handler = CoroutineExceptionHandler { _, exception -> // ArithmeticException will be caught (from 2nd child). // // IOException can be accessed using exception.suppressed (from 1st child) } GlobalScope.launch(handler) { launch { // 1st child try { delay(Long.MAX_VALUE) } finally { throw IOException() } } launch { // 2nd child throw ArithmeticException() } }
Hopefully, the relationship between coroutines and exception is a bit clearer now. You can always refer to the excellent Kotlin docs . Happy throwing!
Recommend
-
60
Exceptions and proxies and coroutines, oh my! 31 July 2019 Checked exceptions are a concept that exist only in the Java compiler and are enforced only in source code. In Java bytecode and at runtime in the...
-
10
bracketing and async exceptions in haskellI've been digging into async exceptions in haskell, and getting more and more concerned. In particular, bracket seems to be often used in ways that are not async exception safe. I've foun...
-
23
Initialization list exceptions and raw pointers What to do when an exception is thrown on the initialization list when allocating memory for a raw pointer?...
-
7
Description Join us for an exceptional conversation with James CTO and Abe Gong CEO of Superconductive . James and Abe share the story of how their startups started as an open source solution...
-
4
Finding the Bug in the Haystack: Hunting down Exceptions in Production
-
7
Testing Exceptions with xUnit and Actions Date Published: 14 April 2021
-
11
Why use exceptions? ¶ Δ What good can using exceptions do for me? The basic answer is: Using exceptions for er...
-
10
Catching and rendering exceptions Error handling in Zope 4 TL;DR: You have to write an exception view in file system code which is rendered when an exception occurs. ...
-
8
Things every Kotlin Developer should know about Coroutines. Part 3: Structured Concurrency.Subscribe to my newsletter and never miss my upcoming articles
-
5
Applying Kotlin Structured Concurrency: Part III — Exceptions in coroutinesIn Java and Kotlin you can use try/catch for catch exceptions.If you don’t handle an exception in a meth...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK