'When to use coroutineScope vs supervisorScope?

Can someone explain what exactly is the difference between these two?

When do you use one over the other?

Thanks in advance.



Solution 1:[1]

The best way to explain the difference is to explain the mechanism of coroutineScope. Consider this code:

suspend fun main() = println(compute())

suspend fun compute(): String = coroutineScope {
    val color = async { delay(60_000); "purple" }
    val height = async<Double> { delay(100); throw HttpException() }
    "A %s box %.1f inches tall".format(color.await(), height.await())
}

compute() fetches two things from the network and combines them into a string description. In this case the first fetch is taking a long time, but succeeds in the end; the second one fails almost right away, after 100 milliseconds.

What behavior would you like for the above code?

  1. Would you like to color.await() for a minute, only to realize that the other network call has long failed?

  2. Or perhaps you'd like the compute() function to realize after 100 ms that one of its network calls has failed and immediately fail itself?

With supervisorScope you're getting 1., with coroutineScope you're getting 2.

The behavior of 2. means that, even though async doesn't itself throw the exception (it just completes the Deferred you got from it), the failure immediately cancels its coroutine, which cancels the parent, which then cancels all the other children.

This behavior can be weird when you're unaware of it. If you go and catch the exception from await(), you'll think you've recovered from it, but you haven't. The entire coroutine scope is still being cancelled. In some cases there's a legitimate reason you don't want it: that's when you'll use supervisorScope.

Solution 2:[2]

I think Roman Elizarov explain it quite in details, but to make it short:

Coroutines create the following kind of hierarchy:

  • Parent Coroutine
    • Child coroutine 1
    • Child coroutine 2
    • ...
    • Child coroutine N

Assume that "Coroutine i" fails. What do you want to happen with its parent?

If you want for its parent to also fail, use coroutineScope. That's what structured concurrency is all about.

But if you don't want it to fail, for example child was some kind of background task which can be started again, then use supervisorScope.

Solution 3:[3]

The major difference is that a coroutineScope will cancel whenever any of its children fail. If we want to continue with the other tasks even when one fails, we go with the supervisorScope. A supervisorScope won’t cancel other children when one of them fails.

Here is a useful link for understanding of coroutine in details:

https://blog.mindorks.com/mastering-kotlin-coroutines-in-android-step-by-step-guide

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Muhammad Zahab Ahmad Khan