Promise Chaining

Summary: in this tutorial, you will learn about the JavaScript promise chaining pattern that chains the promises to execute asynchronous operations in sequence.

Introduction to the JavaScript promise chaining

Sometimes, you want to execute two or more related asynchronous operations, where the next operation starts with the result from the previous one. For example:

First, create a new promise that resolves to the number 10 after 3 seconds:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});Code language: JavaScript (javascript)

Note that the setTimeout() function simulates an asynchronous operation.

Then, invoke the then() method of the promise:

p.then((result) => {
    console.log(result);
    return result * 2;
});Code language: JavaScript (javascript)

The callback passed to the then() method executes once the promise is resolved. In the callback, we show the result of the promise and return a new value multiplied by two (result*2).

Because the then() method returns a new Promise with a value resolved to a value, you can call the then() method on the return Promise like this:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result);
    return result * 2;
}).then((result) => {
    console.log(result);
    return result * 3;
});Code language: JavaScript (javascript)

Output:

10
20

In this example, the return value in the first then() method is passed to the second then() method. You can keep calling the then() method successively as follows:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result); // 10
    return result * 2;
}).then((result) => {
    console.log(result); // 20
    return result * 3;
}).then((result) => {
    console.log(result); // 60
    return result * 4;
});
Code language: JavaScript (javascript)

Output:

10
20
60

The way we call the then() method like this is often referred to as a promise chain.

The following picture illustrates the promise chain:

JavaScript Promise Chaining

Multiple handlers for a promise

When you call the then() method multiple times on a promise, it is not the promise chaining. For example:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result); // 10
    return result * 2;
})

p.then((result) => {
    console.log(result); // 10
    return result * 3;
})

p.then((result) => {
    console.log(result); // 10
    return result * 4;
});Code language: JavaScript (javascript)

Output:

10
10
10

In this example, we have multiple handlers for one promise. These handlers have no relationships. Also, they execute independently and don’t pass the result from one to another like the promise chain above.

The following picture illustrates a promise that has multiple handlers:

JavaScript Promise Chaining - multiple handlers

In practice, you will rarely use multiple handlers for one promise.

Returning a Promise

When you return a value in the then() method, the then() method returns a new Promise that immediately resolves to the return value.

Also, you can return a new promise in the then() method, like this:

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(10);
    }, 3 * 100);
});

p.then((result) => {
    console.log(result);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(result * 2);
        }, 3 * 1000);
    });
}).then((result) => {
    console.log(result);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(result * 3);
        }, 3 * 1000);
    });
}).then(result => console.log(result));
Code language: JavaScript (javascript)

Output:

10
20
60

This example shows 10, 20, and 60 after every 3 seconds. This code pattern allows you to execute some tasks in sequence.

The following modified the above example:

function generateNumber(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num);
    }, 3 * 1000);
  });
}

generateNumber(10)
  .then((result) => {
    console.log(result);
    return generateNumber(result * 2);
  })
  .then((result) => {
    console.log(result);
    return generateNumber(result * 3);
  })
  .then((result) => console.log(result));
Code language: JavaScript (javascript)

Promise chaining syntax

Sometimes, you have multiple asynchronous tasks you want to execute in sequence. In addition, you need to pass the result of the previous step to the next one. In this case, you can use the following syntax:

step1()
    .then(result => step2(result))
    .then(result => step3(result))
    ...
Code language: JavaScript (javascript)

If you need to pass the result of the previous task to the next one without passing the result, you use this syntax:

step1()
    .then(step2)
    .then(step3)
    ...
Code language: CSS (css)

Suppose that you want to perform the following asynchronous operations in sequence:

  • First, get the user from the database.
  • Second, get the services of the selected user.
  • Third, calculate the service cost from the user’s services.

The following functions illustrate the three asynchronous operations:

function getUser(userId) {
    return new Promise((resolve, reject) => {
        console.log('Get the user from the database.');
        setTimeout(() => {
            resolve({
                userId: userId,
                username: 'admin'
            });
        }, 1000);
    })
}

function getServices(user) {
    return new Promise((resolve, reject) => {
        console.log(`Get the services of ${user.username} from the API.`);
        setTimeout(() => {
            resolve(['Email', 'VPN', 'CDN']);
        }, 3 * 1000);
    });
}

function getServiceCost(services) {
    return new Promise((resolve, reject) => {
        console.log(`Calculate the service cost of ${services}.`);
        setTimeout(() => {
            resolve(services.length * 100);
        }, 2 * 1000);
    });
}Code language: JavaScript (javascript)

The following uses the promises to serialize the sequences:

getUser(100)
    .then(getServices)
    .then(getServiceCost)
    .then(console.log);Code language: CSS (css)

Output

Get the user from the database.
Get the services of admin from the API.
Calculate the service cost of Email,VPN,CDN.
300Code language: JavaScript (javascript)

Note that ES2017 introduced the async/await that helps you write code that is cleaner than using the promise chaining technique.

In this tutorial, you have learned about the promise chain that executes multiple asynchronous tasks in sequence.

Quiz

Was this tutorial helpful ?