How do Promises work in Javascript?

How do Promises work in Javascript?

Introduction to promises

A promise is commonly defined as a proxy for a value that will eventually become available.

Promises are one way to deal with asynchronous code, without getting stuck in callback hell.

Async functions use promises behind the scenes, so understanding how promises work is fundamental to understanding how async and await work.

How promises work in brief

Once a promise has been called, it will start in a pending state. This means that the calling function continues executing while the promise is pending until it resolves, giving the calling function whatever data was being requested.

The created promise will eventually end in a resolved state, or in a rejected state, calling the respective callback functions i.e. passed to then and catch, upon finishing.

Let's create a promise

const done = true;

const isItDoneYet = new Promise((resolve, reject) => {
  if (done) {
    const workDone = 'Here is the thing I built';
    resolve(workDone);
  } else {
    const why = 'Still working on something else';
    reject(why);
  }
});

As you can see, the promise checks if the done global constant, if that's true, the promise goes into a resolved state; otherwise, the reject callback is executed.

Consuming a Promise

In the last section, we introduced how a promise is created.

Now, let's see how the promise can be consumed or used.

const isItDoneYet = new Promise(/* ... as above ... */);
// ...

const checkIfItsDone = () => {
  isItDoneYet
    .then(ok => {
      console.log(ok);
    })
    .catch(err => {
      console.error(err);
    });
};

In the above code, running the checkIfItsDone() will specify functions to execute when the isItDoneYer promise resolves(in the then call) or rejects (int the catch call).

Handling errors

If anything in the chain of promises fails and raises an error or rejects the promise, the control then goes to the nearest catch() statement down the chain.

new Promise((resolve, reject) => {
  throw new Error('Error');
}).catch(err => {
  console.error(err);
});

// or

new Promise((resolve, reject) => {
  reject('Error');
}).catch(err => {
  console.error(err);
});

Chaining Promises

A promise can be returned to another promise, creating a chain of promises.

A great example of chaining promises is the Fetch API, which we use to get the resource and queue a chain of promises to execute when the resource is fetched.

Example of chaining promises

const status = response => {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response);
  }
  return Promise.reject(new Error(response.statusText));
};

const json = response => response.json();

fetch('/todos.json')
  .then(status) // note that the `status` function is actually **called** here, and that it **returns a promise***
  .then(json) // likewise, the only difference here is that the `json` function here returns a promise that resolves with `data`
  .then(data => {
    // ... which is why `data` shows up here as the first parameter to the anonymous function
    console.log('Request succeeded with JSON response', data);
  })
  .catch(error => {
    console.log('Request failed', error);
  });

In this example, we call fetch() to get a list of TODO items from the todo.json file found in the domain and we create a chain of promises. Running a fetch() returns a response, which has many properties, and within those we reference:

1. status, a numeric value representing the HTTP status code.
2. statusText, a status message,which is OK if the request succeeded.

response also has a json() method, which returns a promise that will resolve with the content of the body processed and transformed into JSON.

So given those promises, this is what happens: the first promise in the chain is a function that we defined, called status(), that checks the response status and if it's not a success response (between 200 and 299), it rejects the promise.

This operation will cause the promise chain to skip all the chained promises listed and will skip directly to the catch() statement at the bottom, logging the Request failed text along with the error message.

In this case, we return the data JSON processed, so the third promise receives the JSON directly:

fetch('/todos.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  });

So, this was about how Promises work in Javascript. In this blog, we talked about what actually are Promises, how to create them & finally how to consume them.

Do comment your reviews :D