Posted on: Written by: K-Sato
⚠️ This article was posted over 2 years ago. The information might be outdated. ⚠️

Table of Contents

What is a promise?

A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved (e.g., a network error occurred). A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.

How to use it

new Promise creates a promise object with a function that gets executed right after the promise object is called with then, catch or finally. You can, for instance, pass a function that fetches data from a server to new Promise and use the created promise object to call other functions with the data you fetched. The function that is passed to new Promise should call resolve or reject to indicate the state of the promise object.

  • resolve(value) — if the job finished successfully, with result value.
  • reject(error) — if an error occurred, error is the error object.
const DATA = { name: 'K-Sato', age: 24 }

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(DATA), 1000)
})

promise.then(result => console.log(result))

Just changed the name of the promise object.

const DATA = { name: 'K-Sato', age: 24 }

let dataFetch = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(DATA), 1000)
})

dataFetch.then(result => console.log(result))

Properties

The resulting promise object has internal properties:

  • state — initially “pending”, then changes to either “fulfilled” or “rejected”,
  • result — an arbitrary value, initially undefined.

When the executor finishes the job, it should call one of the functions that it gets as arguments:

  • resolve(value) — to indicate that the job finished successfully:
    • sets state to “fulfilled”,
    • sets result to value.
  • reject(error) — to indicate that an error occurred:
    • sets state to “rejected”,
    • sets result to error.

The function passed to new Promise is called the executor. When the promise is created, this executor function runs automatically. The executor should do a job (something that takes time usually) and then call resolve or reject to change the state of the corresponding Promise object.

let promise = new Promise(function(resolve, reject) {
  // the function is executed automatically when the promise is constructed
  // executor
  setTimeout(() => resolve('done'), 1000)
})

then

The syntax is:

promise.then(
  function(result) {
    /* handle a successful result */
  },
  function(error) {
    /* handle an error */
  },
)

The first argument of then is a function that

  • runs when the promise is resolved, and
  • receives the result.

The second argument of then is a function that

  • runs when the promise is rejected, and
  • receives the error.
let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve('done!'), 1000)
})

// resolve runs the first function in .then
promise.then(
  result => alert(result), // shows "done!" after 1 second
  error => alert(error), // doesn't run
)

catch

If we’re interested only in errors, then we can use null as the first argument: .then(null, errorHandlingFunction). Or we can use .catch(errorHandlingFunction), which is exactly the same:

let promise = new Promise((resolve, reject) => {
 setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// .catch(f) is the same as promise.then(null, f)
promise.catch(alert); // shows "Error: Whoops!" after 1 second
The call .catch(f) is a complete analog of .then(null, f), it’s just a shorthand.

finally

Just like there’s a finally clause in a regular try {...} catch {...}, there’s finally in promises.

The call .finally(f) is similar to .then(f, f) in the sense that it always runs when the promise is settled: be it resolve or reject.

finally is a good handler for performing cleanup, e.g. stopping our loading indicators, as they are not needed anymore, no matter what the outcome is.

Like this:

new Promise((resolve, reject) => {
  /* do something that takes time, and then call resolve/reject */
})
  // runs when the promise is settled, doesn't matter successfully or not
  .finally(() => stop loading indicator)
  .then(result => show result, err => show error)
It’s not exactly an alias of then(f,f) though. There are several important differences:

A finally handler has no arguments. In finally we don’t know whether the promise is successful or not. That’s all right, as our task is usually to perform “general” finalizing procedures.

A finally handler passes through results and errors to the next handler.

For instance, here the result is passed through finally to then:

new Promise((resolve, reject) => {
  setTimeout(() => resolve('result'), 2000)
})
  .finally(() => alert('Promise ready'))
  .then(result => alert(result)) // <-- .then handles the result

And here there’s an error in the promise, passed through finally to catch:

new Promise((resolve, reject) => {
  throw new Error('error')
})
  .finally(() => alert('Promise ready'))
  .catch(err => alert(err)) // <-- .catch handles the error object

That’s very convenient because finally is not meant to process a promise result. So it passes it through.

References

About the author

I am a web-developer based somewhere on earth. I primarily code in TypeScript, Go and Ruby at work. React, RoR and Gin are my go-to Frameworks.