louiscad
12/26/2018, 5:38 PMrace
function that would take a list of Deferred
, await for the single fastest (biased or not, doesn't matter), cancel all the "losers", and return the value of the winner. Did someone already solve this problem, or has a clue on how to make this? Thank you!gmariotti
12/26/2018, 5:47 PMSelect
on a list of deferred?louiscad
12/26/2018, 5:51 PMlouiscad
12/26/2018, 5:53 PMselect
thing. I still didn't succeed in wrapping my head around it, and consequently I don't use it…gmariotti
12/26/2018, 6:04 PMselect
allows you to wait on multiple coroutines at the same time and give you back the result of the first one that completes. If there was a version of it that gets a list of suspending functions, in this case Deferred<T>::await
, you can get the result of the first one and then cancel all coroutines in the list with a forEach { it.cancel() }
. But I’m not sure this is possible and, more importantly, how exceptions need to be handledaltavir
12/26/2018, 6:31 PMbdawg.io
12/26/2018, 6:32 PMsuspend fun <T> List<Deferred<T>>.race(): T = select {
forEach {
it.onAwait {
it
}
}
}
altavir
12/26/2018, 6:33 PMlouiscad
12/26/2018, 6:35 PMbdawg.io
12/26/2018, 6:35 PMsuspend fun <T> List<Deferred<T>>.race(): T = select {
forEach {
it.onAwait { result ->
forEach { other ->
if (other != it) {
other.cancel()
}
}
result
}
}
}
louiscad
12/26/2018, 6:36 PMselect
technique! I'll bdawg.io
12/26/2018, 6:37 PMother != it
part. You can invoke cancel
on a completed jobbdawg.io
12/26/2018, 6:40 PMfun <T, C : Iterable<T>> C.cancelAll() = onEach { it.cancel() }
louiscad
12/26/2018, 10:30 PMprivate suspend fun <T> List<Deferred<T>>.raceWithSelect(): T = select {
forEach { it.onAwait { result -> forEach(Deferred<T>::cancel); result } }
}
Albert
12/27/2018, 8:18 PMDeferred
required? Because an alternative could be to send the winning race to a channel or callback, which could cancel a job, some psuedo code:
val racingJob = Job()
val racingActor = actor<Race> {
val winningRace = channel.receive()
racingJob.cancel()
// Do something with winninRace
}
repeat(10) {
launch(racingJob) {
racingActor.send(Race())
}
}
This way you don't have to manage the jobs independentlylouiscad
12/27/2018, 8:42 PMDeferred
makes it easy to get the value (if winning), or be cancelled otherwise. Using a channel or an actor is not natural for my use case which is awaiting for the user to make an action on the user interface in front of a choice and get a value associated with the choice made when resuming the coroutine. I added the following top level function relying on the one I posted above:
suspend inline fun <T> raceOf(vararg racers: Deferred<T>): T = racers.asList().raceWithSelect()
And I use it this way:
val someValue: SomeType = raceOf(
async {
firstButton.awaitOneClick(hideAfterClick = true) // Cancellable and finally hides the View
firstValue
},
async {
secondButton.awaitOneClick(hideAfterClick = true) // Cancellable and finally hides the View
secondValue
}
)
Albert
12/27/2018, 9:10 PMval winningValue = CompletableDeferred<T>()
val racingActor = actor<Race> {
val winningRace = channel.receive()
racingJob.cancel()
winningValue.complete(winningRace)
}
But I could imagine it is still unnatural for the type application you are writing.bdawg.io
12/27/2018, 10:16 PM