Let’s get straight to the point:
Of course, you could just use something like Async.js’s
queue. But we suffer from
I couldn’t find much documentation on the semantics of
it seems that the promise’s function parameter is executed before
the function returns. The thunk is necessary to work around this, to
avoid executing the queued action (
Also, there’s no provision for handling failing actions. Not
production code, blah blah blah. See update note at the top of this
It has 3 interfaces:
Queue an action that takes 2 parameters (identical to the signature for the parameter to
Promise). The action must eventually call one of
reject, otherwise the queue will deadlock.
Queue an action that takes no parameters, and returns a promise (or rather, something that is thenable).
Queue an action that returns nothing, or a value without a
thenattribute. This case is assumed to indicate a synchronous action.
Uses 1 and 2 are for asynchronous actions, that will not have
completed when the function to trigger the action returns (for
XMLHTTRequest). In these cases, give the action a way
(well, two) to signal completion once the action does complete.
“Show Your Working”
My first hack used an array to store each queued action as a promise, but I realised that the only part of the array that is every useful is the last item. So we only need to keep the most recent action.
But, how do we start it off?
Well, we start with a Hah, no need! We’re using promises, so
we can just start off with an already completed action, via
null queue, and
then have a condition that…
Next, we just need to keep track of the the end of the queue. This only changes when we enqueue an action. To rephrase the problem:
Perform the action passed to
enqueue()after the currently executing action completes.
Luckily, it appears that anything passed to
then() of an already
resolved promise will not be executed until the next time through the
Results in output of:
inside outside then-ed
Throw our action’s new promise (technically, the
then()of the current promise.
Make the new promise the one we mean when we say the currently executing action. We can do this because from the point of view of the enqueued action, all of the previous actions are the currently executing action. Serial queue, remember?
For setting up the thunk, we can take advantage of the reason we need the thunk to help us:
We need to create a function that will execute the action, and then resolve the promise. But we can’t do that until we’re in the middle of creating the promise. We can actually tease this apart, so that it’s clearer what is going on:
So we use the promise as the end of the queue, but actually use the thunk as the thing that is executed when the current action completes.
Put it all together, et voilà. A queue that will serialise asynchronous actions. No warranty expressed or implied.