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 function
s, 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
AsyncPoll
class. See PR for usage. It does exactly as I hoped.So it turns out, there is an
Awaitable
calledConditionWaitHandle
withsucceed
andfail
methods* that can be invoked by any context (so long as the underlying WaitHandle hasn't expired yet), forcing theConditionWaitHandle
to resolve with the passed values.I gave the code a hard look, and underneath it all, it works by successive
Awaitable
races, whichConditionWaitHandle
permits. More specifically, the collection ofAwaitable
s is compressed viaAwaitAllWaitHandles
(aka\HH\Asio\v
) which resolves as slowly as the slowestAwaitable
, then nested within aConditionWaitHandle
. EachAwaitable
is awaited in anasync function
that triggers the commonConditionWaitHandle
, concluding the race. This is repeated until theAwaitable
s 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
ConditionWaitHandle
meshes with the philosophy that allAwaitable
s should run to completion.