Dale Jefferson

Dale L. Jefferson

TypeScript, JavaScript, React, React Native

Error first pattern for JavaScript Async/Await

By Dale Jefferson. Published
JavaScript, Async Await, Error Handling, TypeScript

Dealing with errors is not fun. In this article, I will show you a pattern for dealing with errors in Async/Await. This pattern avoids the use of try/catch.

// Callback
fetch(URL, (error, response) => ...)

// Promise
fetch(URL).then((response) => ...).catch((error) => {})

// Async / await
try { fetch(URL) } catch (error) {}

Notice with callbacks you have no choice but to deal with the error, or at least feel guilty of not dealing with it.

The problem

async fetchData() {
    let json;

    try {
        const response = await fetch(URL)
        try {
             json = await response.json()
        } catch (error) {
             console.log("Parsing failed", error)
             return
        }
    } catch (error) {
        console.log("Request failed", error)
        return
    }

    const stars = json.stargazers_count
    this.setState({stars})
}

Yuck, I hope that disgusts you as it does me, exceptions should be exceptional, and there is nothing exceptional about connectivity issues on a mobile device, this is normal control flow.

Go To Statement Considered Harmful  (Edsger Dijkstra - 1968)

I believe try / catch is a glorified Go To statement, execution stops and control jumps to the catch block. It should be used sparingly for exceptions not normal control flow.

So we have gone from explicate returned errors to throwing exceptions, this does not sound like progress.

A possible solution

async fetchData() {
    const [responseError, response] = await fetch(URL);

    if (responseError || !response) {
        console.log("Request failed", responseError)
        return
    }

    const [parseError, json] = await response.json()

    if (parseError || !json) {
        console.log("Request failed", parseError)
        return
    }

    const stars = json.stargazers_count
    this.setState({stars})
}

This uses array destructuring and since error is the first value it makes you think about handling errors. I’ve mixed Go error handling with NodeJS error first callbacks.

Implementation details

const errorFirstPromise = promise => {
  return new Promise(resolve => {
    return promise
      .then(result => {
        return resolve([null, result]);
      })
      .catch(error => {
        return resolve([error, null]);
      });
  });
};

Update: I’ve published a npm module for this pattern: https://www.npmjs.com/package/error-first