Как дождаться выполнения Promise в Node.js?

Ответ

В Node.js я использую несколько подходов для работы с Promise в зависимости от контекста:

1. Async/await (рекомендуемый подход)

async function getUserData(userId) {
  try {
    // Ждем выполнение промиса
    const user = await User.findById(userId);

    // Параллельное выполнение независимых промисов
    const [orders, settings, logs] = await Promise.all([
      Order.find({ userId }),
      UserSettings.findOne({ userId }),
      AccessLog.find({ userId }).limit(10)
    ]);

    return { user, orders, settings, logs };
  } catch (error) {
    console.error('Failed to fetch user data:', error);
    throw new Error(`User data unavailable: ${error.message}`);
  }
}

2. Promise chaining для последовательных операций

function processUpload(filePath) {
  return validateFile(filePath)
    .then(metadata => resizeImage(metadata))
    .then(resized => uploadToS3(resized))
    .then(s3Url => saveToDatabase(s3Url))
    .then(result => {
      console.log('Upload completed:', result.id);
      return result;
    })
    .catch(error => {
      console.error('Upload pipeline failed:', error);
      cleanupTempFiles(filePath);
      throw error;
    });
}

3. Ожидание нескольких промисов с разными стратегиями

async function fetchWithFallbacks(urls) {
  // Promise.race - первый успешный или ошибочный
  const fastest = await Promise.race(urls.map(url => fetch(url)));

  // Promise.allSettled - все, независимо от результата
  const allResults = await Promise.allSettled(
    urls.map(url => fetch(url).then(r => r.json()))
  );

  const successful = allResults
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value);

  return { fastest: await fastest.json(), successful };
}

4. Ожидание в не-async контекстах

// В обработчике события или callback
server.on('request', (req, res) => {
  // Используем IIFE (Immediately Invoked Function Expression)
  (async () => {
    try {
      const data = await processRequest(req);
      res.writeHead(200);
      res.end(JSON.stringify(data));
    } catch (error) {
      res.writeHead(500);
      res.end('Internal Server Error');
    }
  })();
});

// Или с .then() в цепочке
readFilePromise('config.json')
  .then(config => startServer(config))
  .then(server => {
    console.log('Server started on port', server.port);
    return server;
  })
  .catch(error => {
    console.error('Failed to start server:', error);
    process.exit(1);
  });

5. Top-level await (Node.js 14.8+)

// В ES-модулях можно использовать await на верхнем уровне
import { connectToDatabase } from './db.js';

const db = await connectToDatabase();
console.log('Database connected');

export { db };

Из моего опыта:

  • Всегда обрабатываю ошибки через try/catch с async/await
  • Для параллельных независимых операций использую Promise.all()
  • Для операций, где нужен первый результат, использую Promise.race() с timeout
  • В legacy-коде с callback иногда использую util.promisify()
  • Избегаю смешивания async/await и .then() в одной функции для читаемости

Ответ 18+ 🔞

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

1. Async/await — это наше всё, ёпта

async function getUserData(userId) {
  try {
    // Ждём, пока промис отстреляет
    const user = await User.findById(userId);

    // А тут делаем овердохуища запросов параллельно
    const [orders, settings, logs] = await Promise.all([
      Order.find({ userId }),
      UserSettings.findOne({ userId }),
      AccessLog.find({ userId }).limit(10)
    ]);

    return { user, orders, settings, logs };
  } catch (error) {
    console.error('Не вышло достать данные юзера:', error);
    throw new Error(`Данные пользователя недоступны: ${error.message}`);
  }
}

2. Цепочки промисов для последовательного говна

function processUpload(filePath) {
  return validateFile(filePath)
    .then(metadata => resizeImage(metadata))
    .then(resized => uploadToS3(resized))
    .then(s3Url => saveToDatabase(s3Url))
    .then(result => {
      console.log('Загрузка завершена:', result.id);
      return result;
    })
    .catch(error => {
      console.error('Всё накрылось медным тазом:', error);
      cleanupTempFiles(filePath);
      throw error;
    });
}

3. Ждём несколько промисов по-разному, как хитрая жопа

async function fetchWithFallbacks(urls) {
  // Promise.race — кто первый, того и тапки
  const fastest = await Promise.race(urls.map(url => fetch(url)));

  // Promise.allSettled — всем сестрам по серьгам
  const allResults = await Promise.allSettled(
    urls.map(url => fetch(url).then(r => r.json()))
  );

  const successful = allResults
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value);

  return { fastest: await fastest.json(), successful };
}

4. Ожидание там, где async нельзя, блядь

// В каком-нибудь обработчике события
server.on('request', (req, res) => {
  // Используем IIFE, чтоб не бздеть
  (async () => {
    try {
      const data = await processRequest(req);
      res.writeHead(200);
      res.end(JSON.stringify(data));
    } catch (error) {
      res.writeHead(500);
      res.end('Сервер ебнулся');
    }
  })();
});

// Или старым добрым .then()
readFilePromise('config.json')
  .then(config => startServer(config))
  .then(server => {
    console.log('Сервер запущен на порту', server.port);
    return server;
  })
  .catch(error => {
    console.error('Сервер не взлетел:', error);
    process.exit(1);
  });

5. Top-level await (Node.js 14.8+) — красота, ядрёна вошь!

// В ES-модулях можно на самом верху await впендюрить
import { connectToDatabase } from './db.js';

const db = await connectToDatabase();
console.log('База подключена');

export { db };

Из моего горького опыта, чувак:

  • Всегда ловлю ошибки через try/catch, а то будет тебе хиросима
  • Для параллельного говна — Promise.all(), ебать копать
  • Если нужен первый результат — Promise.race() с таймаутом, чтоб не ждать вечность
  • В старом коде иногда util.promisify() выручает, как палочка-выручалочка
  • Стараюсь не мешать async/await и .then() в одной функции — а то получается пиздопроебина, в которой нихуя не разберёшься