5.4 Promises

The promises API allow us to represent an operation that has not been performed yet. Promises are supported by some web browsers today but it is recommended to use a promises library like Q.js or Bluebird. These libraries are compatible with older browsers and include some helpers that will help us to minimize the complexity of our asynchronous code.

Getting Ready

All you need to implement this recipe is an installation of TypeScript 2.0 or higher and bluebird. We can install bluebird and its type declaration files using npm:

$ npm install bluebird --save
$ npm install @types/bluebird –-save-dev

How to do it

We are going to re-implement the example that we implemented in the recipe about callbacks in this chapter. This time we are going to use promises instead of callbacks:

import * as Promise from 'bluebird';

function doSomethingAsync() {
  return new Promise((resolve, reject) => {
    console.log('do something...');
    resolve('result');
  });
}

How it works

To create a promise, we need to create an instance of the Promise class. Some browsers include native support for the Promise class but we are going to use the bluebird promises library. The function named doSomethingAsync returns an instance of the Promise class. Promise objects have an estate that can be pending, fulfilled or rejected. When we create an instance of the Promise class the promise estate is pending. Once a promise has been fulfilled or rejected its state cannot change again. The Promise class constructor takes a function as its only argument, which takes two functions names resolve and reject as its arguments:

  • Resolve: When this function is invoked the promise state changes from pending to fulfilled, which means that the promise operation was completed successfully. We can pass the result of the operation performed by the promise to this function.
  • Reject: When this function is invoked the promise estate changes from pending to rejected, which means that something went wrong during the execution of the promise. The details abut the error can be passed to this function.

    It is recommended to append “Async” at the end of the named of asynchronous functions (functions that return a promise) as a naming convention because it helps developers to understand the behavior of the function even when no documentation is available.

Inside the promise body we can perform any kind of operation. In the preceding code snippet, we are just displaying some text in console and invoking the resolve function. If we invoke the function its return will be the promise object with pending state:

const promise = doSomethingAsync();

The promise object allows us to use the methods named then and catch, which allow us to add event handlers that will be invoked when the promise state changes from pending to fulfilled or rejected respectively:

promise.then(function() {
	console.log('done!');
}).catch(function() {
	console.log('error!');
});

It is also possible to do it inline:

doSomethingAsync().then(function() {
	console.log('done!');
}).catch(function() {
	console.log('error!');
});

There’s more

We will now re-implement the AJAX helper that we previously implemented in this chapter using the Promises API:

import * as Promise from 'bluebird';

interface IAjaxOptions {
    url: string,
    async: boolean,
    method: string,
    dataType: string
  }

function ajaxAsync(options: IAjaxOptions) {
    return new Promise((resolve, reject) => {

      const request = new XMLHttpRequest();
      request.open(options.method, options.url, options.async);

      request.onload = function () {
        if (request.status >= 200 && request.status < 400) {
          // Success!
          const data = null;
          if (options.dataType === 'json') {
            const data = JSON.parse(request.responseText);
          }
          resolve(data);
        }
        else {
          // We reached our target server, but it returned an error
          reject(request.responseText);
        }
      };

      request.onerror = (e) => {
        reject(e);
      };

      request.send();
    });
  }

Once we have implemented the function, it can be invoked to access the promise object. We can then use the the then and catch methods as follows:

ajaxAsync({
    url: '/data/countries.json',
    async: true,
    method: 'GET',
    dataType: 'json'
  }).then(function (data) {
    // Success!
    console.log(data);
  }).catch(function (e) {
    // There was a connection error of some sort
    console.log(e);
  });

Source Code

Promises
Promises (AJAX)

See also

Please refer to the other recipes about promises in this chapter to learn about more bluebird and the promises API.


Shiv Kushwaha

Author/Programmer