is there a way to perform “awaitAny” on a collecti...
# coroutines
m
is there a way to perform “awaitAny” on a collection of jobs? i.e wait for first that finishes?
l
@Mikael Alfredsson Do you want to cancel the other ones?
m
yes. first result “wins” the other ones are not necessary after that.
l
Alright, I use this all the time in Android apps, especially UI code (includes infinite suspending loops that I want to run while the race is ongoing)
m
looks nice, thanks.
l
And here's the permalink to the code that powers these
raceOf
and
race
+
launchRacer
functions.
raceOf
is great for most use cases, but if you have a dynamic number of racers, or late racers,
race
+
launchRacer
is the tool you need: https://github.com/LouisCAD/Splitties/blob/1936c6c7e445c048077d8b4b7bb1c13b99e0ca0[…]coroutines/src/commonMain/kotlin/splitties/coroutines/Racing.kt
@elizarov It's been a bunch of times I see folks that need this use case of racing coroutines and cancelling "losers". Do you think
raceOf
and
race
could make it into the API of kotlinx.coroutines in the current form, or one close to it? EDIT: If the answer is yes, I'll open an issue and a PR for that.
o
@louiscad Could you elaborate how you use
race
in Android applications?
nevermind, I clicked on the link... 🙄
😄 1
m
since awaitAny() would be orthogonal with awaitAll(), I would aim for that first (not supporting dynamic racers). Should be easy to argue for in the API.
l
I don't want my API to win, I want the best API to win and stay on the long run, without breaking users of kotlinx.coroutines, and I'm unsure it's the best API.
I don't like
awaitAny()
because its name makes it that it must not cancel the late ones, which is rarely what you want, unless you want to waste resources or have bugs.
m
sometimes you don’t want to cancel them. If you only need the result of one, but you still want the “sideeffects” from the others, then cancel is optional.
l
Can you give the example of a real-world use case where this would be needed?
m
maybe not the normal usecase, but still it would be nice to have that option.
awaitAny
could take a parameter on how to handle the rest. usecase: Send a FCM to all members of the on call support team. We are happy to know that atleast one got it, but the rest should get it if possible
for saving data over multiple nodes, the same can be argued for. We want to know that its saved, but if it can, save to the other ones as well
(im dealing with very flaky transmissions at my current position 🙂 )
l
I don't think you'd want
awaitAny()
for that push notification use case. You'd want to watch live how many are getting it out of how many, so you'd get a
Flow
of a ratio, and you could just use
first { it.receivedCount > 1 }
m
wouldnt that be completely the same as awaitAny but with flows?
l
Then you are asked to evolve to see the progress and the failure rate, and you need to re-architect the whole thing 😅
But yes,
awaitAny()
could do it. The thing is that such an easy way to do it IMHO would lead to people often using the wrong tool, like using
awaitAny()
in places where they should have used
raceOf
or an equivalent that cancels the late ones.
That's why I think it's best to not bring it to kotlinx.coroutines in such a form.
m
awaitAny(CANCEL_WAITING)
or
awaitAny(LEAVE_WAITING)
for example
l
"waiting" blob thinking fast
m
maybe not the best naming, but I guess you get what I’m aimng for?
l
Also, I don't like SCREAMING_CASE, it's distracting and unfair to the rest of the code
Yes, I get it, I always got what you said. I'm also thinking about API naming, knowing it's know to not be easy 😅
One this is that if some code is left uncancelled, it should be explicit/clear from the name of the method.
awaitAny
is explicit, actually, I just fear it's too easy to use it when you don't factor the late runners. Maybe with a required parameter like this?
awaitAny(cancelOthers = true)
WDYT?
m
Im trying to think about other use cases, i.e should it be an enum or a flag, but right now I can’t think of any, so a flag that defaults to
cancelOthers
seems reasonable.
someone else probably can find a case for
awaitN(count = 1, cancelOthers = true)
but I cant find that case now 🙂
l
I don't think it should have a default value, because "await any" alone doesn't imply cancelling. It's like waiting for someone in a store doesn't imply you close the door for others. Hence, having this default behavior (which is still the most common one I believe, for async programming), would be surprising, with the code kinda lying about what's it doing.
m
I like defaults for the most common behaviour, but that might be a personal thing.
anyway, thanks for the
raceOf
code.
blob smile happy 1
l
That's not a personal thing, I also like defaults for the most common behavior, so long it's not suprising or lying when you factor what's actually written.