Ответ
В 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.
Ответ 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. Остальное — либо для специфики, либо когда в старый код влезаешь, и там уже, понимаешь, пизда рулю.