5.6 Chaining promises

When we need to run a few asynchronous operations in certain order, we can use chained promises to use the output of a promise as the input of the following promise.

Getting Ready

All you need to implement this recipe is an installation of TypeScript 2.0 or higher and jQuery.

How to do it

We are going to use once more the getProfileAsync function:

import * as Promise from 'bluebird';
import * as $ from 'jquery';

interface IUserProfile {
    name: string,
    surname: string,
    website: string,
    twitter: string
  }

function getProfileAsync() {
    return new Promise((resolve, reject) => {
      $.ajax({
        url: '/data/user_profile.json',
        async: true,
        method: 'GET',
        dataType: 'json',
        success: function (data: IUserProfile) {
          resolve(data);
        },
        error: function (e) {
          reject(e);
        }
      });
    });
  }

We are also going to declare two new asynchronous operations. The first function transforms the JSON data returned by the getProfileAsync function into HTML. The second function appends the HTML to a HTML element in the DOM.

function generateHTMLAsync(data: IUserProfile) {
    return new Promise((resolve, reject) => {
      const html = `<div>
              <h3>${data.name}</h3>
              <p>${data.name}</p>
              <h3>${data.surname}</h3>
              <p>${data.surname}</p>
              <h3>${data.website}</h3>
              <p>${data.website}</p>
              <h3>${data.twitter}</h3>
              <p>${data.twitter}</p>
            </div>`;
      resolve(html);
    });
  }

function appendHTMLAsync(html: string) {
    return new Promise((resolve, reject) => {
      const $container = $('#user_account');
      $container.html(html);
      resolve($container);
    });
  }

Now that we have three asynchronous tasks we can chain then as follows:

getProfileAsync()
  .then(generateHTMLAsync)
  .then(appendHTMLAsync)
  .catch((e) => {
    console.log(e);
  });

How it works

The promises API allow us to use the then and catch methods to create chain of promises. The preceding code snippet executes the getProfileAsync tasks and when it is fulfilled pass its output as the input of the following tasks in the chain: generateHTMLAsync. Finally, when the generateHTMLAsync tasks has been completed, the appendHTMLAsync task is executed. If any errors take place during the execution the chain of promises, the function passed to the catch method is invoked.

There’s more

In the preceding example we have passed a function which return a promise to the then method but it is also possible to pass a function which returns a value instead. The then method creates a promise using this value. The following code snippet can help us to understand how we can pass the output of a promise as the input of the following promise and what happens when a promise has not fulfillment value:

Promise.resolve('step-1-result').then(
    function step2(preiousResult) {
      console.log(`step 2 args: ${preiousResult}`);
      return 'step-2-result';
    }
  ).then(
    function step3(preiousResult) {
      console.log(`step 3 args: ${preiousResult}`);
      // no return
    }
  ).then(
    function step4(preiousResult) {
      console.log(`step 4 args: ${preiousResult}`);
      return Promise.resolve('step-4-result');
    }
  ).then(
    function step5(preiousResult) {
      console.log(`step 5 args: ${preiousResult}`);
    }
  );

The preceding code snippet displays the following in the browser console when executed:

step 2 args: step-1-result
step 3 args: step-2-result
step 4 args: undefined
step 5 args: step-4-result

Source Code

Chaining promises
Chaining promises (more)

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