In JavaScript, there are two main ways to handle asynchronous code: then/catch (ES6) and async/await (ES7). These syntaxes give us the same underlying functionality, but they affect readability and scope in different ways. In this article, we’ll see how one syntax lends itself to maintainable code, while the other puts us on the road to callback hell!

JavaScript runs code line by line, moving to the next line of code only after the previous one has been executed. But executing code like this can only take us so far. Sometimes, we need to perform tasks that take a long or unpredictable amount of time to complete: fetching data or triggering side-effects via an API, for example.

Rather than letting these tasks block JavaScript’s main thread, the language allows us to run certain tasks in parallel. ES6 saw the introduction of the Promise object as well as new methods to handle the execution of these Promises: then, catch, and finally. But a year later, in ES7, the language added another approach and two new keywords: async and await.

This article isn’t an explainer of asynchronous JavaScript; there are lots of good resources available for that. Instead, it addresses a less-covered topic: which syntax — then/catch or async/await — is better? In my view, unless a library or legacy codebase forces you to use then/catch, the better choice for readability and maintainability is async/await. To demonstrate that, we’ll use both syntaxes to solve the same problem. By slightly changing the requirements, it should become clear which approach is easier to tweak and maintain.

We’ll start by recapping the main features of each syntax, before moving to our example scenario.

then, catch And finally

then and catch and finally are methods of the Promise object, and they are chained one after the other. Each takes a callback function as its argument and returns a Promise.

For example, let’s instantiate a simple Promise:

const greeting = new Promise((resolve, reject) => {
  resolve("Hello!");
});

Using then, catch and finally, we could perform a series of actions based on whether the Promise is resolved (then) or rejected (catch) — while finally allows us to execute code once the Promise is settled, regardless of whether it was resolved or rejected:

greeting
  .then((value) => {
    console.log("The Promise is resolved!", value);
  })
  .catch((error) => {
    console.error("The Promise is rejected!", error);
  })
  .finally(() => {
    console.log(
      "The Promise is settled, meaning it has been resolved or rejected."
    );
  });

For the purposes of this article, we only need to use then. Chaining multiple then methods allows us to perform successive operations on a resolved Promise. For example, a typical pattern for fetching data with then might look something like this:

fetch(url)
  .then((response) => response.json())
  .then((data) => {
    return {
      data: data,
      status: response.status,
    };
  })
  .then((res) => {
    console.log(res.data, res.status);
  });

async And await

By contrast, async and await are keywords which make synchronous-looking code asynchronous. We use async when defining a function to signify that it returns a Promise. Notice how the placement of the async keyword depends on whether we’re using regular functions or arrow functions:

async function doSomethingAsynchronous() {
  // logic
}

const doSomethingAsynchronous = async () => {
  // logic
};

await, meanwhile, is used before a Promise. It pauses the execution of an asynchronous function until the Promise is resolved. For example, to await our greeting above, we could write:

async function doSomethingAsynchronous() {
  const value = await greeting;
}

We can then use our value variable as if it were part of normal synchronous code.

As for error handling, we can wrap any asynchronous code inside a try...catch...finally statement, like so:

async function doSomethingAsynchronous() {
  try {
    const value = await greeting;
    console.log("The Promise is resolved!", value);
  } catch (e) {
    console.error("The Promise is rejected!", error);
  } finally {
    console.log(
      "The Promise is settled, meaning it has been resolved or rejected."
    );
  }
}

Finally, when returning a Promise inside an async function, you don’t need to use await. So the following is acceptable syntax.

async function getGreeting() {
  return greeting;
}

However, there’s one exception to this rule: you do need to write return await if you’re looking to handle the Promise being rejected in a try...catch block.

async function getGreeting() {
  try {
    return await greeting;
  } catch (e) {
    console.error(e);
  }
}

Using abstract examples might help us understand each syntax, but it’s difficult to see why one might be preferable to the other until we jump into an example.

#javascript #programming #web-development #developer

A Comparison Of async/await Versus then/catch
1.70 GEEK