I am trying to implement Rx stream/observable merging with Hack async, and a core step is described by the title. A code version of this step would look something like this:
<?hh // strict
async function foo(Awaitable<Iterable<T>> $collection): Awaitable<void> {
$ordered_generator = async_collection_to_gen($collection) // (**)
foreach($ordered_generator await as $v) {
// do something with each awaited value in the time-order they are resolved
}
}
However, after mulling it over, I don't think I can write the starred (**) function. I've found that at some point or another, the implementations I've tried require functionality akin to JS's Promise.race, which resolves when the first of a collection Promises resolves/rejects. However, all of Hack's Awaitable collection helpers create an Awaitable of a fully resolved collection. Furthermore, Hack doesn't permit that we don't await async calls from async functions, which I've also found to be necessary.
Is it possible to anyone's knowledge?
This is possible actually! I dug around and stumbled upon a fork of asio-utilities by @jano implementing an
AsyncPollclass. See PR for usage. It does exactly as I hoped.So it turns out, there is an
AwaitablecalledConditionWaitHandlewithsucceedandfailmethods* that can be invoked by any context (so long as the underlying WaitHandle hasn't expired yet), forcing theConditionWaitHandleto resolve with the passed values.I gave the code a hard look, and underneath it all, it works by successive
Awaitableraces, whichConditionWaitHandlepermits. More specifically, the collection ofAwaitables is compressed viaAwaitAllWaitHandles(aka\HH\Asio\v) which resolves as slowly as the slowestAwaitable, then nested within aConditionWaitHandle. EachAwaitableis awaited in anasync functionthat triggers the commonConditionWaitHandle, concluding the race. This is repeated until theAwaitables have all resolved.Here's a more compact implementation of a race using the same philosophy:
Very elegant solution, thanks @jano!
*(the semblance to promises/deferred intensifies)
I am curious how premature completion via
ConditionWaitHandlemeshes with the philosophy that allAwaitables should run to completion.