Как дождаться результата выполнения асинхронной операции в 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.

Ответ 18+ 🔞

А, ну вот, опять про эту асинхронность в Node.js. Слушай, тут история, как с выбором между поехать на метро, на такси или пешком с бутылкой пива — все способы довезут, но ощущения, бля, пиздец какие разные.

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);
});
// А этот код-то выполнится ДО того, как файл прочитают, имей в виду

Проблема в чём? Да в том, что если тебе нужно после файла базу запросить, а потом ещё API дернуть, то получается этажерка из колбэков, или, как его называют, «ад колбэков». Читать это — волнение ебать, терпения ноль ебать. Чистая пиздопроебибна.

2. Промисы (Promises) Тут уже поумнее. Это как дать тебе бумажку с обещанием, что результат будет. Многие модули сейчас уже отдают промисы (взять тот же fs.promises).

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

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

Уже лучше, да? Можно цепочки строить, ошибки в одном месте ловить. Но всё равно, когда этих .then() овердохуища, тоже не сахар.

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);
    // И вот тут уже спокойно дальше работай, как будто всё синхронно
  } 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()
  ]);
  // И всё, чувак, все результаты у тебя разом, красота!
}

Если честно, в современных проектах я давно уже на async/await пересел. Читаемость — огонь, ошибки через try/catch ловятся — как родные. В общем, ебать мои старые костыли, но это самый адекватный вариант.