Ответ
В Node.js я применяю несколько паттернов для управления асинхронностью, выбор зависит от контекста:
-
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); } } -
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)); -
Callback (унаследованный код): Функции, передаваемые как аргументы для вызова после завершения операции. Стараюсь избегать из-за "ада колбэков", но понимаю, что многие нативные модули Node.js (
fs,http) построены на них.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.