https://kotlinlang.org logo
#coroutines
Title
# coroutines
d

dstarcev

10/22/2018, 9:20 AM
But it creates a new scope isn't it?
j

Jonathan

10/22/2018, 9:20 AM
Copy code
suspend inline fun <T, R> Iterable<T>.mapAsync(
    crossinline transform: suspend (T) -> R
) = coroutineScope {
  map { async { transform(it) } }.map { it.await() }
}
Yes, and that what you want.
You want
mapAsync
to suspend until all jobs are completed
But you also want
mapAsync
to throw as soon as one job fail.
g

gildor

10/22/2018, 9:21 AM
You can use coroutineScope, but why not just use GlobalScope in this case?
j

Jonathan

10/22/2018, 9:21 AM
And you also want to make sure all job started are canceled in this case
With GlobalScope jobs are not cancelled in case of failure
Global scope just leaks the coroutines
d

dstarcev

10/22/2018, 9:22 AM
I am quite new to coroutines, is it correct for a function to have it's own scope ignoring whatever it is on the caller site?
g

gildor

10/22/2018, 9:23 AM
awaitAll does the trick for you and nothingg will leak
so it’s exactly the use case of awaitAll and documentation explicitly mentions how awaitAll is different from .map { it.await() }
j

Jonathan

10/22/2018, 9:24 AM
Yes true. However, I tend to find
coroutineScope
more explicit
g

gildor

10/22/2018, 9:24 AM
what can be more explicit than awaitAll?
this is exactly what must be used
and coroutineScope even not used in this case
and provides useless scope
is it correct for a function to have it’s own scope ignoring whatever it is on the caller site
No,
coroutineScope
uses
coroutineContext
of outer scope
d

dstarcev

10/22/2018, 9:27 AM
Copy code
suspend inline fun <T, R> Iterable<T>.mapAsync1(
    crossinline transform: suspend (T) -> R
) = coroutineScope { map { async { transform(it) } }.awaitAll() }

suspend inline fun <T, R> Iterable<T>.mapAsync2(
    crossinline transform: suspend (T) -> R
) = map { GlobalScope.async { transform(it) } }.awaitAll()
j

Jonathan

10/22/2018, 9:27 AM
Both solutions are fine
I prefer the first one, but it is opinionated style.
In both cases no coroutine is leaked
g

gildor

10/22/2018, 9:28 AM
There is semantical difference between 2 solutions
d

dstarcev

10/22/2018, 9:28 AM
btw, what difference will it make if I specify the receiver?
Copy code
suspend inline fun <T, R> Iterable<T>.mapAsync2(
    crossinline transform: suspend CoroutineScope.(T) -> R
) = map { GlobalScope.async { transform(it) } }.awaitAll()
g

gildor

10/22/2018, 9:28 AM
coroutineScope solution uses outer context
but GlobalScope uses DefaultDispatcher
j

Jonathan

10/22/2018, 9:29 AM
true
g

gildor

10/22/2018, 9:29 AM
also I would pass dispatcher for this function, so you could use this function for blocking IO
d

dstarcev

10/22/2018, 9:30 AM
in what way?
g

gildor

10/22/2018, 9:31 AM
because solution with GlobalScope in your current version uses Default dispatcher by default
so you shouldn’t use it for blocking code
for blocking code you should use IO explicitly
coroutineScope uses dispatcher of outer scope
so you can wrap it to
withContext(<http://Dispatchers.IO|Dispatchers.IO>)
This will not work for GlobalScope
d

dstarcev

10/22/2018, 9:35 AM
May I add this in the place where I create the initial scope?
Copy code
runBlocking(<http://Dispatchers.IO|Dispatchers.IO>) {
...
}
the intention in my case is to parallelize blocking operations
g

gildor

10/22/2018, 9:54 AM
runBlocking IO doesn’t make any sense in this case
if you usr runBlocking with coroutineScope, you will have 1 (main) thread execute all your tasks
d

dstarcev

10/22/2018, 9:57 AM
In case I write a blocking server and sometimes want to parallelize tasks, what do I do?
g

gildor

10/22/2018, 9:58 AM
Use GlobalScope
+ dispatcher on your choice
2 Views