Promise Chaining

October 7, 2024

Promise Chaining

You may have a case where you have a promise that returns another promise. In this case, you can chain the promises together. This is called promise chaining. You would do this if you had a promise that returned a value that you needed to use in another promise or had a sequence of asynchronous tasks that you needed to complete. Let's take our example from the previous lesson:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    let error = false;

    if (!error) {
      resolve({ name: 'John', age: 30 });
    } else {
      reject('Error: Something went wrong');
    }
  }, 1000);
});

promise
  .then((user) => {
    console.log(user);
  })
  .catch((error) => console.log(error));

So we have a promise that resolves with a user object after 1 second. We're then logging the user object. Whatever we return from the then callback will be passed to the next then callback. So let's return the user's name property. We can then log the name in the next then callback.

promise
  .then((user) => {
    console.log(user);
    return user.name;
  })
  .then((name) => {
    console.log(name);
  })
  .catch((error) => console.log(error));

We can chain as many then callbacks as we want. Let's say we wanted to get the user's name length property. We can do that by returning it and chaining another then callback.

promise
  .then((user) => {
    console.log(user);
    return user.name;
  })
  .then((name) => {
    console.log(name);
    return name.length;
  })
  .then((nameLength) => console.log(nameLength))
  .catch((error) => console.log(error));

We can go on and on like this. I think you'll already see how this is a good way to avoid callback hell, but I'll show you a direct comparison soon.

It's also good to know that if you have a .then() after a .catch(), the .then() will still run. This is because the .catch() only handles errors in the previous .then(). Let's change the error flag to true in the promise. and add a .then() after the .catch().

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    let error = true; // Change this to true

    if (!error) {
      resolve({ name: 'John', age: 30 });
    } else {
      reject('Error: Something went wrong');
    }
  }, 1000);
});

promise
  .then((user) => {
    console.log(user);
    return user.name;
  })
  .then((name) => {
    console.log(name);
    return name.length;
  })
  .then((nameLength) => console.log(nameLength))
  .catch((error) => console.log(error))
  .then((x) => console.log('This will run no matter what'));

We can even return something from the .catch() callback. This will be passed to the next .then() callback. Let's return a string.

promise
  .then((user) => {
    console.log(user);
    return user.name;
  })
  .then((name) => {
    console.log(name);
    return name.length;
  })
  .then((nameLength) => console.log(nameLength))
  .catch((error) => {
    console.log(error);
    return 123;
  })
  .then((x) => console.log('This will run no matter what', x));

Now that you know how to chain promises, let's look at how this compares to callback hell.