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.

or reach out to me at contact@ctidd.com