liveBook · Manning

Promise

As explained in Get Programming with JavaScript Next: New features of ECMAScript 2015, 2016, and beyond

A JavaScript Promise is an object representing the eventual completion or failure of an asynchronous operation and its resulting value or reason.

What if a request starts now, but the screen needs its data later? A promise is an object representing a value that will become available. Call .then() with a callback to use that value. If the promise is pending, the callback waits; if it has already resolved, the callback still runs, but is scheduled rather than invoked immediately. section 30.0

Load organizations

axios.get(url).then(resp => { const orgs = resp.data; }); First, axios.get starts an AJAX request and returns a promise. When the request succeeds, the callback receives the response. A view can keep that promise when it starts loading, then attach .then() when it is ready to display the organizations. section 30.1

Requests can fail. Supply a second callback to .then(success, error) to handle rejection. For longer sequences, .catch(errorHandler) is shorthand for .then(null, errorHandler); each .then() or .catch() returns a new promise, forming a chain. section 30.2 section 31.4

Promise helpers keep the same promise-based interface across different situations:

  • Promise.resolve(value) supplies an already available value as a resolved promise; Promise.reject(reason) creates a rejected one. section 30.3
  • Promise.all(array) waits for all the promises to resolve; Promise.race(array) waits for the first to resolve. section 30.4
const transactions = {};

getTransaction(id) {
  if (transactions[id]) {
    return Promise.resolve(transactions[id]);          1
  } else {
    const loading = load(`/transactions/${id}`);       2
    loading.then(transaction => {
      transactions[id] = transaction;                  3
    });
    return loading;                                    4
  }
}

getTransaction(405).then(transaction => {
  // do something with transaction
}, err => {                                            5
  // handle error
})
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":4,\"ch\":55},{\"line\":4,\"ch\":56}],[{\"line\":6,\"ch\":55},{\"line\":6,\"ch\":56}],[{\"line\":8,\"ch\":55},{\"line\":8,\"ch\":56}],[{\"line\":10,\"ch\":55},{\"line\":10,\"ch\":56}],[{\"line\":14,\"ch\":15},{\"line\":14,\"ch\":16}],[{\"line\":14,\"ch\":17},{\"line\":14,\"ch\":18}],[{\"line\":16,\"ch\":55},{\"line\":16,\"ch\":56}]]"}
!@%STYLE%@!

Follow both paths through getTransaction: a cache hit returns Promise.resolve(cachedValue), while a miss returns the loading promise. Either way, the caller uses .then() and can provide an error handler. section 30.3

Make a promise from a callback

To wrap image loading, return new Promise((resolve, reject) => { /* start loading */ }). Connect the image's onload handler to resolve and its onerror handler to reject. The returned promise now represents the outcome of that load, so callers can handle success or failure with .then(). section 31.1

function fade(image) {
  return new Promise(resolve => {                             1
    function fadeIn(alpha) {                                  2
      draw(image, alpha);                                     3
      if (alpha >= .99) {
        resolve();                                            4
      } else {
        alpha += (1-alpha)/24;                                5
        window.requestAnimationFrame(() => fadeIn(alpha));    5
      }
    }
    fadeIn(0.1);                                              6
  });
}
!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":1,\"ch\":62},{\"line\":1,\"ch\":63}],[{\"line\":7,\"ch\":18},{\"line\":7,\"ch\":19}],[{\"line\":11,\"ch\":13},{\"line\":11,\"ch\":14}],[{\"line\":2,\"ch\":62},{\"line\":2,\"ch\":63}],[{\"line\":7,\"ch\":27},{\"line\":7,\"ch\":28}],[{\"line\":3,\"ch\":62},{\"line\":3,\"ch\":63}],[{\"line\":5,\"ch\":62},{\"line\":5,\"ch\":63}],[{\"line\":7,\"ch\":28},{\"line\":7,\"ch\":29}],[{\"line\":7,\"ch\":62},{\"line\":7,\"ch\":63}],[{\"line\":8,\"ch\":62},{\"line\":8,\"ch\":63}],[{\"line\":7,\"ch\":62},{\"line\":7,\"ch\":63}],[{\"line\":8,\"ch\":62},{\"line\":8,\"ch\":63}],[{\"line\":11,\"ch\":62},{\"line\":11,\"ch\":63}]]"}
!@%STYLE%@!

A canvas fade returns a promise that resolves only when its animation reaches the finishing opacity. This makes completion of an animation usable like completion of a request. section 34.2

Try combining three requests: wait for a credit-availability request and whichever of two credit-score requests finishes first. Which helper should combine the scores, and which should combine that result with credit availability? section 30.4

How Get Programming with JavaScript Next: New features of ECMAScript 2015, 2016, and beyond covers it

How deep it goes
Works through consumption, errors, helpers, chains, and constructor-based wrappers; stops short of detailed event-loop internals.
Examples
Demonstrates GitHub organization requests, cached transactions, image loading, and a canvas fade.
What it assumes
Assumes familiarity with JavaScript callbacks and functions while teaching promise consumption and creation explicitly.
What you can do after
Build it from scratch with new Promise, or apply it via axios and Promise helpers to coordinate asynchronous work.
How it connects
Connects promises to AJAX, callbacks, error handling, memoization, and animation.

Also explained in

Compare how 7 books explain promise →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Get Programming with JavaScript Next: New features of ECMAScript 2015, 2016, and beyond. The section links point to the parts of the book it draws on.