Promises & Asynchronicity in JavaScript

Intro

Promise() is built into JavaScript since ES6.

Promise() constructor function is a built-in JavaScript object an instance of which represents a promise of returning a value. Such a promise is pending until it is settled through being resolved (aka fulfilled) or rejected.

Creating a Promise

A promise in JavaScript is created through using of Promise() constructor function. The constructor accepts two callback functions. The first one is to be executed if the code instructions underpinning the promise succeed and the second one if they fail. The first callback function's argument is the value which is returned by the promise if it resolves (aka fulfils). The second callback function's argument is the error that shall be thrown by the promise if the promise is rejected.

const twoAndTwoPromise = new Promise(function(resolve, reject) {
  const sum = 2 + 2
  if (sum === 4) {
    resolve('2 and 2 does equal 4')
  } else {
    reject(Error('2 and 2 does not equal 4!'))
  }
})

The above promise is always fulfilled as 2 and 2 always equals 4 (in JavaScript). However, when a more elaborate instructions are provided - such as connecting with external resources - they might as well fail for many reasons such as unexpected response status or a network error. Then the reject callback can be used to reject the whole promise.

Asynchronicity of a Promise

When a XMLHttpRequest() request is being made within a promise the promise does not block other instructions from outside its body to be executed when the request is pending. Instead, once the request is completed (successfully or not) only then the resolve or reject callback functions are being executed.

const getUrl = function(url) {
  return new Promise(function(resolve, reject) {
    const request = new XMLHttpRequest()
    request.open('GET', url)
    request.onload = function() {
      if (request.status === 200) {
        resolve(request.response)
      } else {
        reject(Error(`Unable to get ${url}!`))
      }
    }

    request.onerror = function() {
      reject(Error('Network error!'))
    }

    request.send()
  })
}

console.log(getUrl('https://soundof.it/edit-page/javascript-tutorial'))
console.log('After promise!')

// Promise {<pending>}
// After promise!

The promise is in a state of pending during the time after the its execution but before its fulfilment or rejection.

Using Values Returned by Promises

Then

As already noted, once a promise is executed the directly subsequent code from outside its body is being executed before the promise is fulfilled or rejected.

const promiseValue = getUrl('https://www.codelumi.com')

console.log(promiseValue)
console.log('Here!')

// Promise {<pending>}
// Here!

To use a value returned by a promise upon its fulfilment the Promise.prototype.then() needs to be appended to the promise.

getUrl('https://www.codelumi.com') // getUrl() returns a promise.
.then(value => console.log(value))

console.log('Here!')

// Here!
// <!doctype html> ...

Promise.prototype.then() accepts two callback functions as arguments. The first callback is called when the promise is fulfilled and the second when the promise is rejected.

getUrl('https://www').then(value => console.log(value), error => console.log(error))
console.log('Here!')

// Here!
// Error: Network error! ...
// => GET https://www/ net::ERR_NAME_NOT_RESOLVED

Catch

Instead of using the second callback of Promise.prototype.then() to reach the promise error the error can also be caught using Promise.prototype.catch() which accepts only one callback function dedicated to the error handling.

getUrl('https://www').catch(error => console.log(error))
console.log('Here!')

// Here!
// Error: Network error! ...
// => GET https://www/ net::ERR_NAME_NOT_RESOLVED

Finally

There is also Promise.prototype.finally() which is called irrespective of whether the promise was resolved or rejected.

getUrl('https://www')
.catch(error => console.log(error))
.finally(() => console.log('It\'s finally over!'))
console.log('Here!')

// Here!
// Error: Network error! ...
// It's finally over!
// => GET https://www/ net::ERR_NAME_NOT_RESOLVED

Promise Composition aka Promise Chaining

If one of the callback handler functions provided to Promise.prototype.then() or Promise.prototype.catch():

  • returns a value - then then() returns a promise that is to be fulfilled with that value,

  • returns a pending promise - then then() returns the promise,

  • throws an error - then then() returns a rejected promise with the error of the rejected promise.

As then() and catch() return promises themselves they can be chained with subsequent then()s and/or catch()es.

getUrl('https://soundof.it/javascript-tutorial')
.then(value => {
  console.log(value)
  return getUrl('https://soundof.it/ruby-tutorial')
})
.then(value => {
  console.log(value)
})

// => Promise {<pending>}
// <!doctype html> ... <title>JavaScript Tutorial</title> ...
// <!doctype html> ... <title>Ruby Tutorial</title> ...

`Promise()` Static Methods

Promise() constructor function object comes with many built-in static methods, such as:

  • resolve(value) - returns a promise object that is to be resolved with the provided value,

  • reject(error) - returns a promise object that is to be rejected with the provided error,

  • all(promises) - tries to resolve all promises,

  • any(promises) - tries to resolve at least one promise from the promises,

  • allSettled(promises) - settles all promises, irrespective of whether they resolve or reject,

  • race(promises) - waits for the settlement of the first promise.

Async / Await

async and await functionality was introduced by ECMAScript 2017.

When a promise is appended with then() the code within one of the then() callback functions will always be executed after the code following directly the promise chain.

getUrl('https://soundof.it/javascript-tutorial')
.then(value => {
  console.log(value)
})

console.log('Outside of the promise!')

// Outside of the promise chain!
// <!doctype html> ...

async and await keywords allow for asynchronous promise-based behaviour to be written in a way mimicking a synchronous code.

async function getUrlAndLogIt(url) {
  console.log(await getUrl(url))
  console.log('Outside of the promise!')
}

console.log('Outside of the async function getUrlAndLogIt!')

getUrlAndLogIt('https://soundof.it/javascript-tutorial')

// Outside of the async function getUrlAndLogIt!
// <!doctype html> ...
// Outside of the promise!

The code following the await keyword within an async function is not executed until the promise prepended with the await keyword is settled.

An async function implicitly returns a promise that is resolved with the value explicitly returned by the async function unless rejected due to an uncaught exception.

We use cookies and similar technologies to enhance the quality of services, maintain statistics and adjust marketing content. You will find more information in the Cookies Policy.

By clicking OK you grant consent to processing of your personal data by us and our Trusted Partners with the purpose of maintain statistics and adjustment of the marketing content pursuant to the Privacy Policy. If you wish to not grant that consent and/or limit its extent click Settings.