But it creates a new scope isn't it?
# coroutines
d
But it creates a new scope isn't it?
j
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
You can use coroutineScope, but why not just use GlobalScope in this case?
j
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
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
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
Yes true. However, I tend to find
coroutineScope
more explicit
g
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
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
Both solutions are fine
I prefer the first one, but it is opinionated style.
In both cases no coroutine is leaked
g
There is semantical difference between 2 solutions
d
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
coroutineScope solution uses outer context
but GlobalScope uses DefaultDispatcher
j
true
g
also I would pass dispatcher for this function, so you could use this function for blocking IO
d
in what way?
g
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
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
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
In case I write a blocking server and sometimes want to parallelize tasks, what do I do?
g
Use GlobalScope
+ dispatcher on your choice