Simplify JavaScript Promises

Techniques to simplify writing promises in JavaScript

I love promises. Not from people, but from JavaScript.

Share

I love promises. Not from people, but from JavaScript. Promises make your code concise and simple, resulting in easier to understand codebases.

You may also be familiar with the async/await syntax, but unfortunately it causes some headaches. I'll walk through some techniques that solve common scenarios.

Combining async/await with Promise.then

The first problem that I encountered is the verbosity of using fetch:

const response = await fetch('/api');
const data = response.json();

If you're relying solely on just using await, then you will end up using multiple variables and lines for very simple use cases.

Instead, we can take advantage of the "traditional" Promise.then syntax:

const data = await fetch('/api').then(res => res.json());

A one-liner that is still readable and functions the same way.

Combining async/await with Promise.catch

The second problem that I encountered is the scope created with try { } blocks:

try {
  const data = await fetchData();
} catch (error) {
  console.error(error);
}

// Oh no, `data` is undefined 😱
console.log(data);

Hmm... we can't read data outside of the try { } block. If you're new to the const variable I suggest you read my demystifying const variables article, but essentially this variable is scoped to only be used inside of its curly braces { }.

One thing we could do is to lift the variable up:

let data;

try {
  data = await fetchData();
} catch (error) {
  console.error(error);
}

// Now we can use `data` 😎
console.log(data);

But... we are no longer within the safe bounds of using const variables. Anywhere later on in the code, data could get reassigned and we'd be spending hours debugging. Is there a way to get the same result while still using const?

Why yes, there is:

const data = await fetchData()
  .catch(error => {
    console.error(error);
    return null;
  });

// We can still use `data` 👍
console.log(data);

We're again taking advantage of two syntaxes for a Promise: async/await and Promise.catch. If fetchData resolves successfully, then that value is set to the data variable as usual. Otherwise, the data variable gets set to null which is what gets returned inside of .catch().

Refactor wisely

When a new language feature comes out, developers rush to make their codebase follow that new syntax. As you saw in this article, this is not always wise. Combining older syntax with the new can be a powerful way to keep your codebase simple and easy to understand for anyone new to it.