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));

So what is a Promise?

Conceptually, invoking asyncFunc() is a blocking function call.

is a blocking function call. A Promise is both a container for a value and an event emitter.

Conceptually: calling a Promise-based function is blocking #

function asyncFunc ( ) { return new Promise ( ( resolve, reject ) => { setTimeout( () => resolve( 'DONE' ), 100 ); }); } async function main ( ) { const x = await asyncFunc(); 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:

Line (A): Wait until asyncFunc() is finished.

is finished. Line (B): Then log its result 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.

A Promise is a container for an asynchronously delivered value #

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(); setTimeout( () => { const x = blank[ 0 ]; 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.

A Promise is an event emitter #

Another way to view a Promise is as an object that emits events.

function asyncFunc ( ) { const eventEmitter = { success : [] }; setTimeout( () => { for ( const handler of eventEmitter.success) { handler( 'DONE' ); } }, 100 ); return eventEmitter; } asyncFunc() .success.push( x => console .log( 'Result: ' +x));

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.

Further reading #