Simplifying Promises in JavaScript

JavaScript Promises are easy to overcomplicate. Keep them simple and avoid writing unnecessary code.

ES2017’s async/await syntax is already widely adopted among JavaScript developers, but working with Promises directly and cleanly is a necessary underlying skill.

Avoid redundant Promise construction

Here we have an unnecessary Promise being created to wrap the result of a fetch():

function getDataAsync(url) {
  return new Promise((resolve, reject) => {
    const data = fetch(url).then(response => response.json());
    resolve(data);
  });
}

Because fetch itself is a function that returns a Promise, there’s no need to create a new Promise wrapping its result. The result is already resolved through an existing Promise. We can instead reuse the existing Promise returned from the fetch call:

function getDataAsync(url) {
  return fetch(url).then(response => response.json());
}

Whenever we’ve given a Promise, we can use the Promise we’re given. Following from this principle, note that Promise.then() itself returns a Promise. Even when we return this resulting Promise from a function we’ve implemented, we can continue chaining onto it with subsequent then() and catch() calls.

Take an opportunity to simplify

Constructing a Promise is fairly complex. We first need to pass the Promise constructor a function that can accept two callbacks — commonly known as resolve and reject, then implement some functionality and resolve on success or reject with an error. Not only is this a good amount to mental overhead, it also involves a certain amount of of syntactic baggage surrounding the functionality we care about.

In the following example, we create a Promise to simulate the act of getting data over a network. Instead of using an existing API that returns a Promise for us, we really do need to create our own:

function getMockDataAsync(shouldSucceed) {
  return new Promise((resolve, reject) => {
    if (!shouldSucceed) {
      reject(new Error('An error occurred.'));
    }
    resolve({ foo: 'bar' });
  });
}

However, there’s a simpler way to create a Promise that immediately resolves or rejects:

function getMockDataAsync(shouldSucceed) {
  if (!shouldSucceed) {
    return Promise.reject(new Error('An error occurred.'));
  }
  return Promise.resolve({ foo: 'bar' });
}

Seen here, Promise.resolve() and Promise.reject() are static methods on the Promise class that return a Promise and resolve or reject it. These methods are pleasant to use compared to constructing a Promise manually. These methods are great to have on hand when unit testing and mocking Promise-based functionality.

Related articles

Beyond allowing the await keyword to be used within them, JavaScript’s async functions have their own useful aspects. As an alternative to Promise.resolve() and Promise.reject(), consider using async functions to cleanly wrap return values in Promises for convenient mocks in your test suite and to ensure consistent return types.

2018
JavaScript
TypeScript

How to integrate build-time or server-side syntax highlighting for markdown code fences with two libraries: markdown-it and Highlights (Atom’s syntax highlighting engine).

2017
Node.js
JavaScript
Performance

A Firefox issue where right click on a button results in a click event listener firing because of event delegation performed automatically by a JavaScript SPA framework.

2017
Firefox
JavaScript