Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use coroutineScope vs supervisorScope?

Tags:

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

When do you use one over the other?

Thanks in advance.

like image 713
Archie G. Quiñones Avatar asked Dec 02 '18 06:12

Archie G. Quiñones


People also ask

What is the use of CoroutineScope?

Kotlin coroutines provide an API that enables you to write asynchronous code. With Kotlin coroutines, you can define a CoroutineScope , which helps you to manage when your coroutines should run. Each asynchronous operation runs within a particular scope.

When should I use GlobalScope coroutines?

Quoting definition of Global Scope from Kotlin's documentation– “Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely.” GlobalScope creates global coroutines and these coroutines are not children of some specific scope.

What is supervisor scope in Kotlin?

supervisorScopeThis function returns as soon as the given block and all its child coroutines are completed. Unlike coroutineScope, a failure of a child does not cause this scope to fail and does not affect its other children, so a custom policy for handling failures of its children can be implemented.

What is viewModelScope in Android?

viewModelScope contributes to structured concurrency by adding an extension property to the ViewModel class that automatically cancels its child coroutines when the ViewModel is destroyed.


1 Answers

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.

like image 145
Marko Topolnik Avatar answered Oct 20 '22 01:10

Marko Topolnik