We know how to execute parallel suspend functions in a CoroutineScope
, but how to do it within a suspend function that has a return type.
The Problem Link to heading
For what we know it is not hard to run multiple suspend functions parallelly in a CoroutineScope
using async {}
block. async
’s return type is Deferred<T>
which then can be awaited so it is possible to chain multiple Deferred<T>
jobs using awaitAll
which solves the problem of parallelism.
suspend fun taskList1() = "Some text data"
suspend fun taskList2() = "Another random data"
suspend fun parallelNoReturnType() {
CoroutineScope(Dispatchers.IO).launch {
val task1 = async { taskList1() }
val task2 = async { taskList2() }
val total: List<String> = awaitAll(task1, task2)
// process data & return unit
}
}
Certainly, this is not a problem but what if the return type of the function is other than Unit
meaning there is need for a non Unit
return type. Certainly returning the total
variable from above will not work as it is in the @launch
scope, also returning the CoroutineScope
will not work since it’s return type is Job
not List<String>
which we are expecting.
// Eg: 1 - Returning total variable (will not compile)
suspend fun parallelReturnType() : List<String> {
CoroutineScope(...).launch {
...
val total: List<String> = ...
return total // Error
}
}
// Eg: 2 - Returning CoroutineScope (will not compile)
suspend fun parallelReturnType() : List<String> {
// Valid return type but it's not List<String>; Error
return CoroutineScope(...).launch {
...
val total: List<String> = ...
total // hoping this would be the result just like `run` function
}
}
Solution Link to heading
The easy fix is to use coroutineScope { }
suspend function which has a signature public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R
.
For us the R
is List<String>
, using it in the above code requires minimal edit.
suspend fun parallelReturnType() : List<String> = coroutineScope scope@{
val task1 = async { taskList1() }
val task2 = async { taskList2() }
val total: List<String> = awaitAll(task1, task2)
return@scope total
}
The coroutineScope
uses the default coroutineContext
associated with the suspend function i.e the parent’s coroutine context which was used to invoke this suspend function.
If you want to dispatch the jobs on some other CoroutineDispatcher
you can use .async {}
on CoroutineScope
.
suspend fun parallelReturnType() : List<String> = CoroutineScope(Dispatchers.Default).async {
val task1 = async { taskList1() }
val task2 = async { taskList2() }
val total: List<String> = awaitAll(task1, task2)
return@async total
}.await() // important otherwise the return type will be Deferred<List<String>>