Coroutine async
returns Deferred<T>
and there are example of lazy execution and usages of await.
However how can we wait for any one of the Deffered
instances to complete?
In a nutshell
// whats the equivalent of CompletableFuture.anyOf(...)?
// is this how we do it? if so how costly is this?
select<Unit> {
deffered1.onAwait {}
deffered2.onAwait {}
}
Deferred is some kind of analog of Future in Java: in encapsulates an operation that will be finished at some point in future after it's initialization. But is also related to coroutines in Kotlin. From documentation: Deferred value is a non-blocking cancellable future — it is a Job that has a result.
In order to migrate to the async/await pattern, you have to return the async() result from your code, and call await() on the Deferred , from within another coroutine. By doing so, you can remove callbacks you used to use, to consume asynchronously provided values.
A coroutine is a concurrency design pattern that you can use on Android to simplify code that executes asynchronously. Coroutines were added to Kotlin in version 1.3 and are based on established concepts from other languages.
For variety, mentioning .merge(): Flow<T>
alternative to select()
, which I used to address a similar problem (get the first in a collection of deferreds to complete with a non-null value), i.e.
val firstCompletedResult = deferreds.map { it::await.asFlow() }.merge().first()
or if you like,
suspend fun <T> Iterable<Deferred<T>>.awaitAny(): T = map { it::await.asFlow() }.merge().first()
Use select expression like this
val deferred1: Deferred<String?> = GlobalScope.async { getValue1() }
val deferred2: Deferred<String?> = GlobalScope.async { getValue2() }
val deferred3: Deferred<String?> = GlobalScope.async { getValue3() }
val deferredList = listOf(deferred1, deferred2, deferred3)
val firstCompletedResult = select<String?> {
deferredList.forEach {
it.onAwait {}
}
}
Log.d(TAG, "firstCompleted: $firstCompletedResult")
Probably not the safest way to do things, but something like this should work:
inline suspend fun <T> Iterable<Deferred<T>>.awaitAny(): T {
var completed: T? = null
forEachIndexed { index, deferred ->
deferred.invokeOnCompletion {
completed = deferred.getCompleted()
forEachIndexed { index2, deferred2 ->
if (index != index2) {
deferred2.cancel(it)
}
}
}
}
forEach {
try {
it.await()
} catch (ignored: JobCancellationException) {
// ignore
}
}
return completed!!
}
Proof: The following prints 1000
launch(CommonPool) {
// 10 - 1 second(s)
val deferredInts = List(10, {
val delayMs = (10 - it) * 1000
async(CommonPool) {
delay(delayMs)
delayMs
}
})
val first = deferredInts.awaitAny()
println(first)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With