Async/await~ Basics ~

Async/await

The topic of asynchrony in JavaScript is huge. That is because the language offers many mechanisms for handling asynchronous operations. Over the years, those mechanisms change. Historically, the native APIs introduced the very first pattern. They accept a function called a callback that is fired sometime in the future.

const fs = require('fs');

fs.readFile('./content.md', function callback(error, result) {
  if (error) {
    // in case of error
  } else {
    // use the data
  }
})

The callback function is triggered when the operation has some development. Either we read the file successfully, or there is an error. It was a well-established practice to accept the potential error as a first argument. This encourages error handling. Using a lot of callbacks, though, leads to deeply nested functions, producing the so-called "callback hell".

functionA(param, function (err, result) {
  functionB(param, function (err, result) {
    functionC(param, function (err, result) {
      // And so on...
    });
  });
});

The API that was supposed to fix the callback hell was promises. The promise is an object that represents the future of our operation. It may succeed or fail. That object has three states - resolved, rejected, and pending.

Here's the same example converted to use promises.

import { readFile } from 'fs/promises';
readFile('./content.md')
  .then(data => { ... })
  .catch(err => { ... });

The promises don't have the problem of the callbacks, but they are not perfect. Once we start nesting or chaining promises, we may end up with a messy code again.

Nowadays, almost everyone uses another API - the async/await. This API allows us to define a function as asynchronous with the async keyword in front. Then inside, we can place the await keyword before each promise. This will make the function pause till the promise is resolved. Here's the same example but re-written with async/await.

import { readFile } from 'fs/promises';

async function getContent() {
  try {
    const data = await readFile('./content.md');
    // use the data
  } catch(err) {
    // in case of error
  }
}

getContent();

An important fact is that every async function results into a promise. For example, the getResult below doesn't return 10, but a promise that is resolved with a value of 10.

async function getResult() {
  return 10;
}
console.log(getResult());
// Promise<fulfilled: 10>