Какие способы работы с асинхронным кодом знаешь в Node.js?

Ответ

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

  1. Async/Await (предпочтительный способ): Синтаксический сахар над Promise, который делает асинхронный код похожим на синхронный.

    const fs = require('fs').promises;
    async function processFiles() {
      try {
        const data = await fs.readFile('./data.json', 'utf8');
        const parsed = JSON.parse(data);
        await fs.writeFile('./output.json', JSON.stringify(parsed, null, 2));
      } catch (error) {
        console.error('Ошибка обработки файла:', error);
      }
    }
  2. Promise: Объекты, представляющие результат (успешный или неудачный) асинхронной операции. Использую для оборачивания callback-функций или композиции операций.

    const fetch = require('node-fetch');
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => console.log(data))
      .catch(err => console.error('Fetch failed:', err));
  3. Callback (унаследованный код): Функции, передаваемые как аргументы для вызова после завершения операции. Стараюсь избегать из-за "ада колбэков", но понимаю, что многие нативные модули Node.js (fs, http) построены на них.

    const fs = require('fs');
    fs.readFile('./old-config.txt', 'utf8', (err, data) => {
      if (err) throw err;
      console.log(data);
    });
  4. Event Emitters и Streams: Для событийно-ориентированных и потоковых операций (файлы, сетевые запросы).

    const readStream = fs.createReadStream('./huge-file.log');
    readStream.on('data', (chunk) => processChunk(chunk));
    readStream.on('end', () => console.log('File read complete'));

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

Ответ 18+ 🔞

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

Async/Await (мой фаворит, ёпта): Это как волшебный пендель для читаемости кода. Синтаксический сахар поверх промисов, который делает асинхронщину почти как синхронный код. Красота, ядрёна вошь!

const fs = require('fs').promises;
async function processFiles() {
  try {
    const data = await fs.readFile('./data.json', 'utf8');
    const parsed = JSON.parse(data);
    await fs.writeFile('./output.json', JSON.stringify(parsed, null, 2));
  } catch (error) {
    console.error('Ошибка обработки файла:', error);
  }
}

Promise: Ну, классика жанра. Объекты, которые обещают тебе результат — то ли удачный, то ли пиздец. Использую, когда нужно старые колбэки обернуть или операции скомпоновать. Без них никуда.

const fetch = require('node-fetch');
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error('Fetch failed:', err));

Callback (наследие тёмных времён): Функции, которые ты суёшь как аргумент, чтоб их потом вызвали. Стараюсь обходить стороной, потому что это прямой путь в ад колбэков, но, бля, куда денешься — половина нативных модулей Node.js на них завязана. Доверия к ним — ноль ебать.

const fs = require('fs');
fs.readFile('./old-config.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

Event Emitters и Streams: А вот это для особых случаев, когда работаешь с событиями или потоками данных. Типа, огромный файл читаешь или сетевую воронку слушаешь. Тут уже своя философия, чувак.

const readStream = fs.createReadStream('./huge-file.log');
readStream.on('data', (chunk) => processChunk(chunk));
readStream.on('end', () => console.log('File read complete'));

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