Для чего используются async/await в JavaScript/Node.js?

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

Ответ

async/await — это синтаксический сахар над Promises, который позволяет писать асинхронный код в стиле, похожем на синхронный. Это кардинально улучшает читаемость и управляемость асинхронными операциями, такими как запросы к API, работа с файловой системой или базами данных.

Как это работает:

  • Ключевое слово async перед функцией указывает, что она возвращает Promise.
  • Ключевое слово await можно использовать только внутри async-функции. Оно приостанавливает выполнение функции до тех пор, пока переданный ему Promise не будет разрешен (fulfilled) или отклонен (rejected).

Пример: Чтение файла с использованием fs.promises в Node.js

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

// Без async/await (цепочка then/catch)
function readFileOldWay() {
  fs.readFile('./config.json', 'utf8')
    .then(data => JSON.parse(data))
    .then(config => console.log(config.host))
    .catch(err => console.error('Failed to read config:', err));
}

// С использованием async/await
async function readFileNewWay() {
  try {
    const data = await fs.readFile('./config.json', 'utf8');
    const config = JSON.parse(data);
    console.log('Server host:', config.host);
  } catch (err) {
    console.error('Failed to read config:', err);
  }
}
readFileNewWay();

Ключевые преимущества:

  1. Читаемость: Код становится линейным и легким для следования, исчезает "ад колбэков" и глубоко вложенные цепочки .then().
  2. Обработка ошибок: Можно использовать привычный блок try/catch для перехвата ошибок как из асинхронных операций, так и из синхронного кода внутри async-функции.
  3. Отладка: Стек вызовов (stack trace) становится более понятным по сравнению с цепочками промисов.

Важные нюансы:

  • await блокирует выполнение только текущей async-функции, а не всего потока Node.js. Другие операции в событийном цикле продолжают выполняться.
  • Чтобы выполнить несколько независимых асинхронных операций параллельно, используйте Promise.all().
    async function fetchAllData() {
      const [user, posts] = await Promise.all([
        fetchUser(userId),
        fetchPosts(userId)
      ]);
      return { user, posts };
    }

В современной Node.js-разработке async/await является стандартом де-факто для работы с асинхронностью.