Как дождаться результата выполнения асинхронной операции в Node.js?

«Как дождаться результата выполнения асинхронной операции в Node.js?» — вопрос из категории Асинхронность, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Node.js, где все I/O-операции асинхронны по умолчанию, есть три основных подхода для работы с асинхронным кодом.

1. Колбэки (Callbacks) Исторически первый и до сих пор распространенный в базовом API Node.js (например, fs, http). Функция передается как аргумент и вызывается по завершении операции.

const fs = require('fs');

fs.readFile('/path/to/file.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Ошибка чтения файла:', err);
    return;
  }
  console.log('Содержимое файла:', data);
});
// Код здесь выполнится ДО того, как файл будет прочитан

Главный недостаток — «ад колбэков» (callback hell) при вложенных асинхронных операциях.

2. Промисы (Promises) Более современная абстракция, представляющая будущий результат. Многие нативные модули Node.js теперь предоставляют API, возвращающее промисы (например, fs.promises).

const fs = require('fs').promises;

fs.readFile('/path/to/file.txt', 'utf8')
  .then((data) => {
    console.log('Содержимое файла:', data);
  })
  .catch((err) => {
    console.error('Ошибка чтения файла:', err);
  });

Промисы позволяют организовывать цепочки вызовов (chaining) и удобно обрабатывать ошибки.

3. Async/Await Синтаксический сахар над промисами, позволяющий писать асинхронный код в стиле, похожем на синхронный.

const fs = require('fs').promises;

async function readAndProcessFile() {
  try {
    const data = await fs.readFile('/path/to/file.txt', 'utf8');
    console.log('Содержимое файла:', data);
    // Здесь можно продолжить работу с data
  } catch (err) {
    console.error('Ошибка чтения файла:', err);
  }
}

readAndProcessFile();

Ключевые моменты:

  • await можно использовать только внутри функций, объявленных с async.
  • async функция всегда возвращает Promise.
  • Для параллельного выполнения нескольких независимых операций используйте Promise.all().
    async function fetchAllData() {
    const [userData, productList, config] = await Promise.all([
    fetchUser(),
    fetchProducts(),
    loadConfig()
    ]);
    // Обработка всех результатов одновременно
    }

    На практике в современных проектах на Node.js я предпочитаю использовать async/await за его читаемость и простоту обработки ошибок через try/catch.