Hi. I have an issue with `async`, `coroutineScope`...
# coroutines
d
Hi. I have an issue with
async
,
coroutineScope
and exception handling. When
coroutineScope
is moved into separate function, I am able to catch exception inside coroutine. When
coroutineScope
is declared inside coroutine, I am not able to catch exception. This code works fine:
Copy code
suspend fun doWork(): Deferred<String> = coroutineScope {
        async(<http://Dispatchers.IO|Dispatchers.IO>) {
            throw IllegalArgumentException()
        }
    }

    // works fine
    fun loadData() = scope.launch {
        try {
            doWork().await()
        } catch (e: Exception) {
            // exception is caught here
        }
    }
This doesn’t.
Copy code
// crash FATAL EXCEPTION: DefaultDispatcher-worker-3 @coroutine#1
fun loadData() = scope.launch {
    try {
        val deferred = coroutineScope {
            scope.async(<http://Dispatchers.IO|Dispatchers.IO>) {
                throw IllegalArgumentException()
            }
        }
        deferred.await()
    } catch (e: Exception) {
        // exception is NOT caught here
    }
}
g
but those 2 samples are completely different
d
Oops sorry, let me update the code.
g
what is job for your 2nd example?
it’s correct behavior if you use Job instead of SupervisorJob
because any exception of child coroutine also cancels parent coroutine
d
Code updated. I just extracted
coroutineScope
into separate method.
g
Tho this work for you example, but in real code this is really bad code that will not work
Copy code
suspend fun doWork(): Deferred<String> = coroutineScope {
        async(<http://Dispatchers.IO|Dispatchers.IO>) {
First, check docs of coroutineScope, this function returns only when all child coroutines are finished, I belive this is not what you expect, instead pass coroutinecontext from outside
d
From the coroutineScope documentation:
Copy code
* 
 * suspend fun loadDataForUI() = coroutineScope {
 *
 *   val data = async { // <- extension on current scope
 *      ... load some UI data ...
 *   }
 *
 *   withContext(UI) {
 *     doSomeWork()
 *     val result = data.await()
 *     display(result)
 *   }
 * }
 *
Oh I see what do you mean.
g
yes, and it’s fine example, as you can see it uses async just to run some async job inside of this scope,
Second, I don’t know any valid use case for suspend function that returns Deferred
👍 1
Your second example will work if your scope will use SupervisorJob, but would be good to see your real use case, maybe there a better way to handle it, for example extract this code to suspend function and instead catch exceptions of this suspend function instead of wrapping
await()