Reputation: 110
I’m currently learning about Coroutines. I’am following JetBrains’ hands-on: Intro to coroutines and channels. In Structured concurrency section, they mentioned this:
It’s possible to create a new scope without starting a new coroutine, by using the
coroutineScope
function. To start new coroutines in a structured way inside asuspend
function without access to the outer scope, you can create a new coroutine scope that automatically becomes a child of the outer scope that thissuspend
function is called from.
Let’s say from inside a CoroutineScope, I’am calling a function loadUsers. These 3 implementations gave me the same result :
import kotlinx.coroutines.coroutineScope
suspend fun loadUsers(): List<User> {
coroutineScope {
//...
}
}
suspend fun loadUsers(): List<User> {
CoroutineScope(Dispatchers.Default).run {
//...
}
}
suspend fun CoroutineScope.loadUsers(): List<User> {
//...
}
Note: In the body I'am launching multiple coroutines. The full function code could be found in the Hands-on (modified in the question for simplicity).
Could someone answer these 2 questions :
Thanks in advance.
I have seen this already: Difference between CoroutineScope and coroutineScope in Kotlin, but as I mentioned, the 3 styles gave me the same result, so I'm still confused about it.
Upvotes: 1
Views: 2462
Reputation: 93609
3 launches separate coroutines that are not child coroutines. You pretty much should never do this in a suspend function because it forfeits structured concurrency, makes the coroutine context ambiguous within the function, and your function isn’t suspending anyway. I can’t think of any exceptions where it would make sense to want a CoroutineScope argument in a suspend function.
2 is like 3, but even worse because you create a CoroutineScope that is not managed. The coroutine you are launching is not a child or a sibling. It cannot be cancelled under any circumstances. Creating an orphan CoroutineScope like this only to launch a coroutine and then forgetting about it is no different than simply using GlobalScope, which is highly discouraged except when you want to start a task that cannot be cancelled which should be extremely rare. And again, even if you are doing this, it shouldn't be done in a suspend function.
1 is the proper way to launch child coroutines and preserve structured concurrency. The suspend function will actually suspend to await all the child coroutines to finish instead of firing them off asynchronously like in 2 and 3.
3 would make sense for non-suspend functions that are to be treated as asynchronous functions. That said, after using coroutines for a few years, I haven’t ever found a need for a function like that. Seems a little clumsy to me since the behavior is slightly ambiguous or it’s at least a pattern not often used, but it could just be my opinion.
Upvotes: 5