Learnitweb

Introduction to JavaScript Promise

1. Introduction

Suppose you ordered a burger in a restaurant. The cashier will take your order and will promise you that the burger will be delivered. After some time once the burger is available, your burger will be delivered. It is also possible that the burger will not be delivered due to some problem. The cashier will inform you about this with a regret message. This analogy can be used to understand the behavior of Promise in JavaScript.

According to the MDN documentation:

The Promise object represents the eventual completion or failure of an asynchronous object and its resulting value.

Eventual here means, the asynchronous method instead of returning value immediately returns a promise to return a value some point of time in future.

When a promise is created, its value is not necessarily known. A Promise is a proxy for the value returned by the asynchronous method some point in future.

2. Creating a Promise

The constructor syntax for a promise object is:

let promise = new Promise(function(resolve, reject) {
  // executor which produces some results or fails with error
});

Following are important points to be noted:

  • The function passed to the Promise is called the executor. When the Promise is created, executor runs automatically.
  • The executor contains the code which produces a result eventually.
  • The arguments resolve and reject are callbacks provided by the JavaScript itself.
  • resolve and reject expect only one argument (or none). Any additional arguments will be ignored.

Executor contains code which does the processing and returns a result some point in future. When executor obtains the result, it should call one of these callbacks:

  1. resolve(value): If the job (asynchronous method) was successfully executed, with the result value.
  2. reject(error): If the job (asynchronous method) was unsuccessful due to an error, with the error object.

Let us understand this by writing some code. Following is an example of Promise where job was successfully executed.

let promise = new Promise(function(resolve, reject) {
  // executor function which will run automatically
  // after 1 second call the callback 'resolve' with result "done"
  setTimeout(() => resolve("success"), 1000);
});

Following is an example of Promise where an error occurred:

let promise = new Promise(function(resolve, reject) {
  // executor function which will run automatically
  // after 1 second call the callback 'reject' with error object
  setTimeout(() => reject(new Error("Failure")), 1000);
});

3. States of Promise

Promises have three possible mutually exclusive states:

  1. fulfilled: A promise is fulfilled if promise.then(f) will call f “as soon as possible”. Simply, if promise is resolved then it is said to be fulfilled.
  2. rejected: A promise is rejected if promise.then(undefined, f) will call f “as soon as possible.” Simply, if promise is rejected then it is said to be rejected.
  3. pending: A promise is pending if it is neither fulfilled nor rejected.

4. Executor can call either reject or resolve

The exactor should call either resolve or reject. That is, a Promise can result is either a result or an error. Any further calls to resolve or reject are ignored.

let myPromise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    resolve("foo");
    resolve("foo2"); //ignored
    resolve("foo3"); //ignored
    reject("error"); //ignored
  }, 1000);
});

5. Immediately calling resolve/reject

Suppose there is case when the Promise was created, there was no job remaining to be done. In this case you can immediately call resolve or reject.

let promise = new Promise(function(resolve, reject) {
  // immediately calling resolve
  resolve(17); // immediately call resolve with value 17.
}); 

6. Consuming result/error of Promise – try, catch and finally (an introduction)

We’ll now discuss then, catch and finally with respect to promises. We’ll discuss these in brief and will discuss in detail in separate tutorials.

6.1 then

Following is an example of using then. In this case ‘success’ is the result.

let myPromise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    resolve("success");
  }, 1000);
});

myPromise.then(
  function (value) {
    console.log(value); // 'success' will be printed
  },
  function (error) {
    console.log(error);
  }
);

Following is an example of then with reject.

let myPromise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    reject(new Error("Failure"));
  }, 1000);
});

myPromise.then(
  function (value) {
    console.log(value); 
  },
  function (error) {
    console.log(error); // 'failure' will be printed
  }
);

Above can be written using arrow function as well:

myPromise.then(
    value => console.log(value),
    error => console.log(error) 
);

6.2 catch

catch is used to catch errors. If you are only interested in errors, you can use catch.

let myPromise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    reject(new Error("Failure"));
  }, 1000);
});

myPromise.catch((err) => console.log(err));

Another way to handle only errors in using then with first argument as null.

6.3 finally

finally always runs either Promise is resolve or reject. finally is a good place to write cleanup code. One such common use of finally is to use it to stop showing loading indicators on page.

let myPromise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    reject(new Error("Failure"));
  }, 1000);
}).finally(() => {
  console.log("do your cleanup here");
});
myPromise.catch((err) => console.log(err));

Output
--------
do your cleanup here
Error: Failure

6.4 Difference between finally and then(resolve, reject)

You may think that finally is similar to then(resolve, reject), because then with both resolve and reject will always run and will handle. However, there are few differences.

  1. finally does have arguments whereas then has arguments.
  2. finally passes through result to the next handler. For example, in this code, finally runs and does not consume the result which is Error. Error is still handled by the catch.
let myPromise = new Promise(function (resolve, reject) {
  setTimeout(() => {
    reject(new Error("Failure"));
  }, 1000);
}).finally(() => {
  console.log("do your cleanup here");
});
myPromise.catch((err) => console.log(err));