Learn JavaScript Logo

Learn JavaScript

JavaScript Promises Tutorial

Promise on MDN

Promises were added to JavaScript back in 2015 and are now supported on more than 98% of browsers worldwide. A promise allows us to schedule work in the future and lets us handle its eventual success or failure.

The code snippets below are taken from LearnJavaScript.online.

Before learning about promises...

To learn how Promises work, you must be comfortable with the following JavaScript topics:

  • Functions
  • Callback functions

Promise constructor and executor

We'll start by creating a promise that resolves immediately.


new Promise((resolve) => {
  console.log("Promise executor");
  resolve();
});

The code above creates a new Promise object using the Promise constructor. This constructor accepts a function as a parameter. This function is called the executor function.

The executor function receives a resolve function as an argument.

We can call this resolve() function to signify that the Promise completed its work successfully.


console.log("A"); 
new Promise((resolve) => {
  console.log("B");
  resolve();
});
console.log("C");

The Promise executor runs synchronously, as you can see from the output.

We see that the console.log calls run one after the other, verifying that the code is running synchronously.

While the promise allows you to implement asynchronous patterns, the executor function runs synchronously.

Handling successful promises

One of the powers of promises is that they let us handle the eventual success or failure of an asynchronous operation. While the executor is running synchronously, the then method is called asynchronously.

Then .then(callback) allows you to handle the successful case of a promise. It runs synchronously, as you can see from running the code below:


console.log("A"); 
new Promise((resolve) => {
    resolve();
}).then(() => {
    console.log("B");
});
console.log("C");

Rejecting a promise

A promise doesn't always complete successfully. In fact, the function that the executor receives also receives the reject function as a second argument.

We can call reject() to signify that the promise failed.

new Promise((resolve, reject) => {
    reject();
}).then(() => {
    console.log("This won't run anymore because it's not successful");
});
console.log("Done");

Handling rejected/failed promises

You can handle a rejected or failed promise with the .catch() method:

new Promise((resolve, reject) => {
    reject();
}).then(() => {
    console.log("Promise succeeded.");
}).catch(() => {
    console.log("Promise failed.");
});
console.log("Done");

Handling promise completion (success or failure)

You can also run a callback whenever a promise has been completed, whether it resolved successfully or failed using the .finally(callback).


new Promise((resolve, reject) => {
  // 50% chance it will fail
  if (Math.random() < 0.5) {
    resolve();
  } else {
    reject();
  }
}).then(() => {
    console.log("Promise succeeded.");
}).catch(() => {
    console.log("Promise failed.");
}).finally(() => {
    console.log("This runs in both cases, at the end");
});
  

Resolving with data

You can resolve a promise with data. This data will be passed to the .then() callback function.


new Promise((resolve) => {
    setTimeout(() => {
      resolve(42);
    }, 500);
}).then((value) => {
    console.log(value); // 42 (with a 500ms delay)
});

Rejecting with a reason

You can reject a promise with a reason. This reason will be passed to the .catch() callback function.


new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("Something went wrong");
    }, 500);
}).catch((reason) => {
    console.log(reason); // "Something went wrong" (with a 500ms delay)
});

wait(milliseconds) use case

Let's create a function that returns a promise which resolves after milliseconds have elapsed.


function wait(milliseconds) {
    return new Promise((resolve) => {
        setTimeout(resolve, milliseconds);
    });
}

wait(500).then(() => {
    console.log("0.5s elapsed");
});
wait(1000).then(() => {
    console.log("1s elapsed");
});

As you can see, this function returns the result of new Promise(...).

It resolves after milliseconds have elapsed using the setTimeout function.

To consume this function, we called wait(500) and then we could handle its eventual success with the .then(callback).

Benefits of promises

Promises make working with asynchronous code easier. Sure, there will be a learning curve, but once you get the hang of it, you'll find that promises are nice to work with.

Web APIs and promises

Many web APIs, such as the Fetch API, use promises. This means that you can use the .then() and .catch() methods to handle the eventual success or failure of the API call.

Chaining promises

If you've ever worked with the fetch() API, you've probably seen code like this:


fetch('https://jsonplaceholder.typicode.com/todos/1')
    .then(response => response.json())
    .then(data => console.log(data));

The code above chains two promises together:

  1. The first promise is returned by the fetch() function.
  2. The second promise is returned by the response.json() method.

The .then() method is called on the first promise, and it returns a new promise. This new promise is returned by the callback function that you pass to .then().

This is how you can chain promises together. You can keep chaining .then() methods – as long as you're returning a promise from the callback function – to handle the eventual success of the previous promise.

async/await

async/await is a syntactic sugar for promises. Make sure you understand promises first before learning async/await.

You first want to make a function async, this will wrap its return value in a promise:


async function getValue() {
    return 42;
}

getValue().then(value => {
    console.log(value); // 42
});

Once you have an async function, you can use await inside it to unwrap a promise:


function sleep(milliseconds) {
    return new Promise((resolve) => {
        setTimeout(resolve, milliseconds);
    });
}

async function init() {
    await sleep(500);
    console.log("0.5s elapsed");
    await sleep(1000);
    console.log("1s elapsed (after 0.5s wait, so 1.5s total)");
}
init();

Notice that the second await waits for the first one to complete, which is why the total time elapsed for the second console.log is 1.5s.

So, what are promises in JavaScript?

A promise is an object that represents the eventual completion or failure of an asynchronous operation. You create that object with the new Promise() constructor. A promise lets us handle the eventual success or failure of an asynchronous operation with the .then() and .catch() callback methods respectively.

Are you a student of LearnJavaScript.online? Review the Promises chapters.

What do you think of this page? Give your feedback here.

Learn JavaScript LogoPowered by Learn JavaScript