This blog post covers three ways of understanding Promises.
This is an example of invoking a Promise-based function asyncFunc()
:
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('DONE'), 100);
});
}
asyncFunc()
.then(x => console.log('Result: '+x));
// Output:
// Result: DONE
So what is a Promise?
asyncFunc()
is a blocking function call.function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('DONE'), 100);
});
}
async function main() {
const x = await asyncFunc(); // (A)
console.log('Result: '+x);
// Same as:
// asyncFunc()
// .then(x => console.log('Result: '+x));
}
main();
main()
is an async function. Its body expresses well what’s going on conceptually – how we usually think about asynchronous computations:
asyncFunc()
is finished.x
.Prior to ECMAScript 6 and generators, you couldn’t suspend and resume code, which is why, for Promises, you put everything that happens after the code is resumed into a callback. Invoking that callback is the same as resuming the code.
If a function returns a Promise then that Promise is like a blank into which the function will (usually) eventually fill in its result, once it has computed it. You can simulate a simple version of this process via an Array:
function asyncFunc() {
const blank = [];
setTimeout(() => blank.push('DONE'), 100);
return blank;
}
const blank = asyncFunc();
// Wait until the value has been filled in
setTimeout(() => {
const x = blank[0]; // (A)
console.log('Result: '+x);
}, 200);
With Promises, you don’t access the eventual value via [0]
(as in line (A)), you use method then()
and a callback.
Another way to view a Promise is as an object that emits events.
function asyncFunc() {
const eventEmitter = { success: [] };
setTimeout(() => { // (A)
for (const handler of eventEmitter.success) {
handler('DONE');
}
}, 100);
return eventEmitter;
}
asyncFunc()
.success.push(x => console.log('Result: '+x)); // (B)
Registering the event listener (line (B)) can be done after calling asyncFunc()
, because the callback handed to setTimeout()
(line (A)) is executed asynchronously, after this piece of code is finished.
Normal event emitters specialize in delivering multiple events, starting as soon as you register.
In contrast, Promises specialize in delivering exactly one value and come with built-in protection against registering too late: the result of a Promise is cached and passed to event listeners that are registered after the Promise was settled.