DEV Community

Cover image for JavaScript Promises
CiaraMaria
CiaraMaria

Posted on • Updated on

JavaScript Promises

Often technical terminology is more closely related to English than we think. When we make a promise to someone, we say that we will do something and either do it thus resolving the promise or not thus rejecting it.

Alt Text

Promises in Javascript are objects which can be expected to return a value at some point in the future.

A Promise has 3 states:

  • pending: it's initial state of neither resolved nor rejected
  • resolved: the operation was successfully completed
  • rejected: the operation failed

You may already be familiar with Callback functions which are functions passed into another function often used to continue code execution and achieve asynchronous JS. However, callbacks can quickly become nested and difficult to read, resulting in the affectionately named Callback Hell. Promises are syntactic sugar for achieving a similar result. Essentially, it's an object to which you attach callbacks, instead of passing callbacks into a function.

Let's jump into an example with writing our own. In this case, I have promised to write a blog about Promises!

let writeBlog = new Promise((resolve, reject) => {

  // writing blog 

  let blogWritten = true

  if(blogWritten){
    resolve('Completed');
  }
  else {
    reject('Failed')
  }
})

writeBlog.then(resolvedValue => {
  console.log('We are in "then" which means the promise has resolved as: ' + resolvedValue)
})

// => We are in "then" which means the promise has resolved as: Completed
Enter fullscreen mode Exit fullscreen mode

Let's break down what is happening.

We have defined a new Promise, writeBlog. The comment below this represents the code that would be executed to either resolve or reject the promise. In this instance, it resolved since we set blogWritten to true. We then execute the promise and call then()which is a method that returns a Promise and can take up to two arguments. We have given then() an argument of our value for resolve, so our log concatenates the two strings, outputting the full message.

Because then() returns a new Promise, it allows for chaining. For example, if we adjust the execution code above to:

...
writeBlog.then(resolvedValue => {
  console.log('We are in "then" which means the promise has resolved as: ' + resolvedValue)
}).then(() => {
    console.log('This is the second "then!"')
  })

// => We are in "then" which means the promise has resolved as: Completed
This is the second "then!"
Enter fullscreen mode Exit fullscreen mode

Here we chained a second then() which runs chronologically as shown in the output.

What if instead the Promise is rejected? We can use the catch() method which returns a Promise dealing specifically with rejected cases and takes one argument.

let writeBlog = new Promise((resolve, reject) => {

  // writing blog 

  let blogWritten = false

  if(blogWritten){
    resolve('Completed');
  }
  else {
    reject('Failed')
  }
})

writeBlog.then(resolvedValue => {
  console.log('We are in "then" which means the promise has resolved as: ' + resolvedValue)
}).catch(rejectedValue => {
  console.log('We are in "catch" which means the promise has ' + rejectedValue)
})

// => We are in "catch" which means the promise has Failed
Enter fullscreen mode Exit fullscreen mode

Here we used the same code, except we changed blogWritten to false and chained a catch() to our executed Promise. As you can see, the output changes. Because the Promise rejected we see our catch() message.

Other methods available to Promise worth mentioning:

  • Promise.all used when we want multiple promises to execute and wait until all of them are ready. However, it will reject as a whole if any of the Promises rejects
  • Promise.allSettled waits for all promises to settle, regardless of the individual results
  • Promise.race waits only for the first settled promise, regardless of the result

Fetch() and Promise

fetch() allows us to retrieve data similar to XMLHttpRequests and takes one required argument of a URL.

fetch() returns a Promise.

Let's take a basic fetch() example:

fetch("some URL")
.then(response => response.json())
.then(data => //do something with data)
Enter fullscreen mode Exit fullscreen mode

Remember, fetch() returns a Promise which we can then call then() on. One caveat here is that fetch() will always succeed even if we get something like a 404 error. The only time we will see an error in this example is if we encounter a network error. One way we can accommodate this is to check if the response is OK from within our response function.

fetch("some URL")
.then(response => {
   if(!response.ok) {
      console.log('Error!')
   }
   return response.json()
})
...
Enter fullscreen mode Exit fullscreen mode

Guarantees

Promises come with a few guarantees according to MDN:

  • Attached callbacks will never be called before the completion of the current run of the JavaScript event loop, meaning that each one is processed completely before any other.
  • Callbacks added with then() will be called even after the success or failure of the asynchronous operation.
  • Multiple callbacks may be added by chaining then() several times. They executed one after another, in the order in which they were inserted.

Summary

A Promise is an object which we expect to return a value in the future.
A Promise is used to handle the asynchronous result of an operation.
A Promise has three states: pending, resolved, rejected.
Promises have useful methods such as Promise.all, Promise.allSettled, Promise.race
then() returns a Promise.
fetch() returns a Promise.

Top comments (0)