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