.then() vs. await

In the early days of web browsers, Promises and the await keyword didn’t exist. The only way to handle asynchronous I/O was to use “callback functions”—functions you passed as arguments to be executed later (like setTimeout).

When Promises were introduced, the .then() syntax was created as a massive improvement over traditional callbacks. Eventually, the async and await keywords were introduced as a massive improvement over .then().

The Callback Pyramid of Doom

If you need to make sequential asynchronous calls (e.g., fetch a user, then fetch their messages, then fetch the delivery status), using .then() requires chaining multiple nested callbacks.

fetchRecipient()
  .then(function (recipient) {
    return fetchMessageForRecipient(recipient.id);
  })
  .then(function (message) {
    return fetchDeliveryStatus(message.id);
  })
  .then(function (status) {
    console.log(`The status is ${status}`);
  });

The Clean await Solution

By using async/await, you completely flatten the structure. The code reads top-to-bottom exactly like synchronous code.

const recipient = await fetchRecipient();
const message = await fetchMessageForRecipient(recipient.id);
const status = await fetchDeliveryStatus(message.id);

console.log(`The status is ${status}`);

Why do we still see .then()?

The async/await syntax wasn’t added to JavaScript until ES2017. Because of this, there are millions of lines of legacy code in the wild that still rely on .then().

As a modern JavaScript developer, you must know how to read .then() chains, but you should exclusively write new code using async and await whenever possible.