How can I run multiple coroutines in parallel and return only the value of the one that finishes first?
Real-life scenario, I have two data sources - Database and API service. I don't care where does the data originate from, I just need it fast. How can I query both Database and API service and cancel the other request when the one finishes?
In RxJava world this would be equal to Amb operator. How can I achieve similar behaviour using coroutines?
I came up with following implementation:
suspend fun getFaster(): Int = coroutineScope {
select<Int> {
async { getFromServer() }.onAwait { it }
async { getFromDB() }.onAwait { it }
}.also {
coroutineContext.cancelChildren()
}
}
The coroutineScope
acts as a parent to all async calls performed within. After the select
finishes we can just cancel the rest.
You can use select
to write your own amb
operator. Something like that:
suspend fun <T> amb(vararg jobs: Deferred<T>): T = select {
fun cancelAll() = jobs.forEach { it.cancel() }
for (deferred in jobs) {
deferred.onAwait {
cancelAll()
it
}
}
}
You can read more about select
expression here