Try/Catch vs .then().catch()

Handling Promises

Both .then and await help manage the resolution of a promise.

For example, when working with a helper method that makes an HTTP request, we can use .then to structure the code like this:

function getList() {
  const url = "/.netlify/functions/chicagomusiccompass";
  const results = fetch(url).then((response) => response.json());

  return results;
}

Alternatively, we can use await to structure the code like this:

async function getList() {
  const url = "/.netlify/functions/chicagomusiccompass";
  const response = await fetch(url);
  const results = await response.json();

  return results;
}

In both cases, the output remains the same:

[
    {
        "name": "name one"
    },
    {
        "name": "name two"
    },
    ...
]

Note: Both return a promise, so whoever calls the method will need to wait for the promise to be resolved using either .then() or await 🤓.

  • .then()
getList().then((list) => setList(list));
  • await
const list = await getList();
setList(list);

Handling Errors

The real difference comes when handling errors. Up to this point, the implementation looks similar. But what happens if an error is thrown?

With the .then approach, you can handle it like this:

function getList() {
  const url = "/invalid-url";

  const results = fetch(url)
    .then((response) => response.json())
    .catch(() => []);

  return results;
}

With await, the error handling would look like this:

async function getList() {
  const url = "/invalid-url";

  try {
    const response = await fetch(url);
    const results = await response.json();

    return results;
  } catch (error) {
    return [];
  }
}

Once again, both approaches produce the same result:

[];

It’s worth noting that the exception is actually triggered by .json(). The fetch() function always resolves its promise, even for HTTP errors, and communicates issues via the status code. In this case, the /invalid-url response body happens to be invalid JSON, which is why .json() throws an exception.

Also, notice how a default value of [] is provided in both cases. This is recommended because it allows the consumer to avoid handling exceptions and simply respond to the returned value.

Conclusion

It doesn’t really matter which approach you choose, both produce the same output, and the syntax differences are minimal. As with many other things in development, the choice often comes down to preference.

In the teams I’ve worked with, there’s usually a preferred style, and for consistency, everyone tries to stick with it. However, during Pull Requests reviews, there’s rarely any strict policy on whether to use try/catch or .then().catch().

At some point, tools like ESLint or Prettier might enforce a rule or even convert code to a consistent style. AI agents can already do this, but since there’s no functional difference, it’s more about aligning with team consensus. So next time you need to resolve a promise, check if your team has a preference, then go with the flow.