Read more

An intro to Javascript promises

Dominik Schöler
September 06, 2016Software engineer at makandra GmbH

Promises are the new way™ to express "Do this, and once you're done, do that". In contrast to callbacks, promises are easily chainable. From the readme of Q Show archive.org snapshot , an early implementer of the pattern:

The callback approach is called an “inversion of control”. A function that accepts a callback instead of a return value is saying, “Don’t call me, I’ll call you.”. Promises un-invert the inversion, cleanly separating the input arguments from control flow arguments. This simplifies the use and creation of APIs, particularly variadic, REST and spread arguments.

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

Note that promises are not a drop-in replacement for callbacks. Both have situations they shine in, with different implications and drawbacks. As a rule of thumb,

  • use callbacks to delegate control, e.g. in an event handler: "Hey, whenever a user clicks you, please run this code" (keyword "whenever")
  • use promises to make time consuming things async without writing async code, e.g. for animations: "Hey, once you're done with that animation, please run this code (keyword "once")

Promises are built into modern browsers and Microsoft Edge, there are polyfills for Internet Explorer Show archive.org snapshot . Usage:

Usage

Assume this piece of code returns a promise:

api.get('pages') // => Promise

You may now chain function calls to the promise using .then(), without needing to wait for the HTML request to actually return. The promise will collect all functions passed to then() and invoke them once the original promise "resolves".

const storePages = function(pages) {};
const notifyAboutError = function(errorMessage) {};

api.get('pages')
  .then(storePages, notifyAboutError
  .then(setupLinks);

In case a promise could not be fulfilled, the second parameter of then will be invoked as error handler.

Wrapping a callback in a promise

let p = new Promise(resolve, reject);
let promise = p(() => asyncFunctionWithCallback(function(result) {
  if (result === 'success') {
    resolve();
  } else {
    reject();
  }
}));
      
promise.then(() => alert('Promise was resolved!'));

You can experiment with callbacks vs. promises in our curriculum lesson.

Legacy information

jQuery

With jQuery, you would do it like this:

deferred = $.Deferred()
callAsyncFunction (result) ->
  if result == 'success'
    deferred.resolve()
  else
    deferred.reject()

promise = deferred.promise()

promise.then(-> alert('Promise was resolved!'))

In jQuery, the difference between a "Deferred" and a "Promise" is that a deferred has resolve and reject methods in addition to then. A Promise only has then. When you hand back your promise to other code, it's considered good practice to only give back a Promise, never a Deferred.

AngularJS 1.x

In Angular's $q promise library, you would do it like this:

promise = $q (resolve, reject) ->
  callAsyncFunction (result) ->
    if result == 'success'
      resolve()
    else
      reject()
      
promise.then(-> alert('Promise was resolved!'))

$q calls its argument function with two functions resolve and reject. These are used to propagate the outcome of the promise. Call resolve() to fulfill the promise, or reject() if something went wrong.

Inside the argument function you can do whatever you like. Just make sure to call the respective function when you're done.

Dominik Schöler
September 06, 2016Software engineer at makandra GmbH
Posted by Dominik Schöler to makandra dev (2016-09-06 11:56)