How async works in a Promise implementation

Keyvan M. Sadeghi
2 min readMar 10, 2019

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.

--

--

Keyvan M. Sadeghi

Keyvan is co-founder and CEO at Functionland, an open-source startup working on the intersection of Blockchain and AI