I want to run to suspend functions in parallel and return result once the faster one of them returns result. I tried doing the following, but I think it only returns when both of them have finished and it takes too much time, since I'm waiting for both.
val coroutineScope = CoroutineScope(Dispatchers.Main)
val a = coroutineScope.async {
a(context)
}
val b = coroutineScope.async {
b(context)
}
val results = listOf(a, b).awaitAll()
return if (results.any { it is RunSuccess }) {
...
} else {
...
}
Any ideas?
You can use select
as follows :
suspend fun doWork(): String = coroutineScope {
select<String> {
async { work1() }.onAwait { it }
async { work2() }.onAwait { it }
}.also {
coroutineContext.cancelChildren()
}
}
On this example is returning a String
but you can change it with whatever you want, it depends on what your work
is returning.
In case you are looking for more functional programming
version you can use raceN
from Arrow
Where you have this method
public suspend inline fun <A, B> raceN(crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B): Either<A, B> =
raceN(Dispatchers.Default, fa, fb)
And then you call this raceN
method
public suspend inline fun <A, B> raceN(
ctx: CoroutineContext = EmptyCoroutineContext,
crossinline fa: suspend CoroutineScope.() -> A,
crossinline fb: suspend CoroutineScope.() -> B
): Either<A, B> =
coroutineScope {
val a = async(ctx) { fa() }
val b = async(ctx) { fb() }
select<Either<A, B>> {
a.onAwait.invoke { Either.Left(it) }
b.onAwait.invoke { Either.Right(it) }
}.also {
when (it) {
is Either.Left -> b.cancelAndJoin()
is Either.Right -> a.cancelAndJoin()
}
}
}