Register to get access to free programming courses with interactive exercises

Async/Await JS: Asynchronous programming

Despite all the conveniences, promises are not the pinnacle of programming evolution. Let's look back at the disadvantages they add:

  • It has its own error handling, which bypasses try/catch. This means that both ways of processing errors will appear in the code, combining in bizarre ways
  • Sometimes, you need to pass data down the chain from the highest levels, and it's not very convenient to do it with promises. We have to create variables outside the promise
  • It's still easy to end up with nested code when using promises if you don't keep an eye on it

All these difficulties can be dealt with by the async/await mechanism, which makes code with promises look even more like synchronous code! Remember our task of merging two files. Here's the code:

import fsp from 'fs/promises';

const unionFiles = (inputPath1, inputPath2, outputPath) => {
  let data1;
  return fsp.readFile(inputPath1, 'utf-8')
    .then((content) => {
      data1 = content;
    })
    .then(() => fsp.readFile(inputPath2, 'utf-8'))
    .then((data2) => fsp.writeFile(outputPath, `${data1}${data2}`));
};

Now let's look at the same code using async/await. I want to emphasize that async/await works with promises:

import fsp from 'fs/promises';

const unionFiles = async (inputPath1, inputPath2, outputPath) => {
  // A very important point. Just as in the example above, these requests are executed strictly one after the other
  // (although this doesn't block the program, it means that other code can also be executed during these requests)
  const data1 = await fsp.readFile(inputPath1, 'utf-8');
  const data2 = await fsp.readFile(inputPath2, 'utf-8');
  await fsp.writeFile(outputPath, `${data1}${data2}`);
};

This version is virtually the same as its synchronous version in terms of appearance. The code is so simple that you can't even believe it's not synchronous. Let's break it down in order.

The first thing we see is the key word async before the function definition. It means that this function always returns a promise: const promise = unionFiles(...). And now we don't need to return the result of this function explicitly, it will become a promise anyway.

The key word await is used within the function before calling functions that, in turn, also return promises. If the result of this call is assigned to a variable or constant, the result of the call is written to it. If there is no assignment to a variable or constant, as in the last await call, then the operation waits to be executed without its result being used.

Asynchrony in this case (as with promises) guarantees that the program isn't blocked while waiting for calls to complete, it can carry on doing something else (but not in this function). But it does not guarantee that things happen in parallel. Moreover, consecutive await awaits within the same function are always performed strictly one after the other. The easiest way to understand this is to think of the code as a chain of promises, where each successive operation is performed within then.

What about error handling? Now all you have to do is add the usual try/catch and the errors will be caught!

import fsp from 'fs/promises';

const unionFiles = async (inputPath1, inputPath2, outputPath) => {
  try {
    const data1 = await fsp.readFile(inputPath1, 'utf-8');
    const data2 = await fsp.readFile(inputPath2, 'utf-8');
    await fsp.writeFile(outputPath, `${data1}${data2}`);
  } catch (e) {
    console.log(e);
    throw e; // throwing it again, because the code that calls the function must be able to catch the error
  }
};

However, when executing promises in parallel, you can't do without the Promise.all function:

const unionFiles = async (inputPath1, inputPath2, outputPath) => {
  // These calls start reading almost simultaneously and don't wait for each other
  const promise1 = fsp.readFile(inputPath1, 'utf-8');
  const promise2 = fsp.readFile(inputPath2, 'utf-8');
  // Now we wait for them both to finish
  // The data can be unpacked straight away
  const [data1, data2] = await Promise.all([promise1, promise2]);
  await fsp.writeFile(outputPath, `${data1}${data2}`);
};

To summarize, the async/await mechanism makes the code as flat and synchronous as possible. It makes it possible to use try/catch and easily manipulate data from asynchronous operations.

// Code with callbacks
import fs from 'fs';

fs.readFile('./first', 'utf-8', (error1, data1) => {
  if (error1) {
    console.log('boom!');
    return;
  }
  fs.readFile('./second', 'utf-8', (error2, data2) => {
    if (error2) {
      console.log('boom!');
      return;
    }
    fs.writeFile('./new-file', `${data1}${data2}`, (error3) => {
      if (error3) {
        console.log('boom!');
      }
    });
  });
});

// Code with promises
import fsp from 'fs/promises';

let data1;
fsp.readFile('./first', 'utf-8')
  .then((d1) => {
    data1 = d1;
    return fsp.readFile('./second', 'utf-8');
  })
  .then((data2) => fsp.writeFile('./new-file', `${data1}${data2}`))
  .catch(() => console.log('boom!'));

// Code on async/await
import fsp from 'fs/promises';

// In real life, it's better to read files in parallel, like in the function 
const data1 = await fsp.readFile('./first', 'utf-8');
const data2 = await fsp.readFile('./second', 'utf-8');
await fsp.writeFile('./new-file', `${data1}${data2}`);

Recommended materials

  1. Real code example from Hexlet projects

Hexlet Experts

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs

From a novice to a developer. Get a job or your money back!

Frontend Developer icon
Profession
beginner
Development of front-end components for web applications
start anytime 10 months

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.