How async works in a Promise implementation
Handling asynchronous scenarios in a Promise implementation can be mind-blowing. Not so much if you embrace the functional programming paradigm. Though guess I fell short of properly explaining how it works exactly in Nancy, evident by the questions I received via direct messages and comments on social media. I try to explain a bit more here, assuming you commanded the Nancy article.
To refresh our memory, here is the final code:
In acync scenarios, resolve
or reject
are not called immediately. In our example:
const delay = milliseconds => new Nancy(resolve => setTimeout(resolve, milliseconds));
resolve
will be called after milliseconds
by setTimeout
.
What happens to the users who invoked then
and catch
before milliseconds
is over?
const afterPromise = delay(500).then(() => console.log('done'));
They should get a Promise immediately, let’s call it after Promise. So there are two Promises here, one that we created by new Nancy(…
(invoking constructor in delay
's definition), let’s call it this
Promise, and the after Promise (from the code line above).
The after Promise should resolve after 500 milliseconds
. How do we do that?
Remember, changeState(states.pending)
is already called in the constructor of this
Promise. Hence, this.state
would remain at states.pending
for the duration of milliseconds
. So then
returns a call to callLater
which in turn returns the after Promise:
const callLater = getMember => callback => new Nancy(resolve => laterCalls.push(() => resolve(getMember()(callback))));
We push
a void function (() =>
) to the latterCalls
array of this
Promise.
In the body of the this void function, resolve(this.then(callback))
will be invoked later.
When should we call our void function? After the call to the resolve
of this
Promise by setTimeout
. Where is that in our code? resolve
calls getCallback
of this
Promise which in turn calls apply
. So at the end of apply
:
for (const laterCall of laterCalls) {
laterCall();
}
And since this
Promise is now (after milliseconds
) at a resolved state, this.then
in then: callLater(() => this.then)
has its value assigned to a tryCall
, so our laterCall
becomes:
() => resolve(tryCall(() => console.log('done')))
console.log('done')
executes and the after Promise resolves.
Same logic holds for calls to catch
before milliseconds
, of course with the ignore mechanism (_ => this
) instead.
Hope this’s helped, by all means let me know if anything needs further clarification.