← Назад к вопросам
В чем преимущество async/await и промисов перед callback в Node.js?
2.0 Middle🔥 131 комментариев
#Node.js и JavaScript
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
В чем преимущество async/await и промисов перед callback в Node.js?
Это эволюция асинхронного кода в JavaScript. Каждый этап приносит улучшения в читаемость, обработку ошибок и управление потоком исполнения.
Этап 1: Callbacks (Адский ад)
// Callback Hell / Pyramid of Doom
fs.readFile('file.txt', (err, data) => {
if (err) {
console.error(err);
} else {
db.query('SELECT * FROM users', (err, users) => {
if (err) {
console.error(err);
} else {
users.forEach(user => {
const message = `User: ${user.name}`;
notify.send(message, (err, result) => {
if (err) {
console.error(err);
} else {
console.log('Notification sent');
}
});
});
}
});
}
});
Проблемы:
- Невероятно сложно читать
- Обработка ошибок дублируется везде
- Трудно контролировать поток
- Hell callback: каждый уровень добавляет отступ
Этап 2: Promises (Лучше, но сложно)
fs.promises.readFile('file.txt', 'utf-8')
.then(data => {
return db.query('SELECT * FROM users');
})
.then(users => {
const promises = users.map(user => {
const message = `User: ${user.name}`;
return notify.send(message);
});
return Promise.all(promises);
})
.then(() => {
console.log('All notifications sent');
})
.catch(err => {
console.error('Error:', err);
});
Улучшения:
- Линейный поток кода
- Единая обработка ошибок (catch)
- Можно использовать Promise.all, Promise.race
- Лучше читается
Проблемы:
- Всё ещё не очень интуитивно
- Promise chains становятся сложными
- Трудно понять логику при 5+ .then()
Этап 3: async/await (Почти как синхронный код)
async function processData() {
try {
const data = await fs.promises.readFile('file.txt', 'utf-8');
const users = await db.query('SELECT * FROM users');
for (const user of users) {
const message = `User: ${user.name}`;
await notify.send(message);
}
console.log('All notifications sent');
} catch (err) {
console.error('Error:', err);
}
}
processData();
Преимущества:
- Выглядит как обычный синхронный код
- Легко читать и понимать
- try/catch как в синхронном коде
- Не нужно .then() цепочки
Сравнительная таблица
| Параметр | Callback | Promise | async/await |
|---|---|---|---|
| Читаемость | Ужасна | Хорошая | Отличная |
| Обработка ошибок | Повторяется везде | Единая (catch) | try/catch |
| Логика потока | Запутана | Понятна | Максимально понятна |
| Debugging | Сложно | Средне | Легко |
| Performance | Хорош | Хорош | Идентичен Promise |
Практический пример: Обработка ошибок
Callback (ПЛОХО):
function fetchUser(id, callback) {
getUserFromDB(id, (err, user) => {
if (err) {
// Какую-то ошибку потеряем
callback(err);
return;
}
getPostsByUser(user.id, (err, posts) => {
if (err) {
// Снова обработка
callback(err);
return;
}
callback(null, { user, posts });
});
});
}
Promise (ЛУЧШЕ):
function fetchUser(id) {
return getUserFromDB(id)
.then(user => {
return getPostsByUser(user.id)
.then(posts => ({ user, posts }));
})
.catch(err => {
// Единая обработка всех ошибок
console.error('Fetch failed:', err);
throw err;
});
}
async/await (ЛУЧШЕЕ):
async function fetchUser(id) {
try {
const user = await getUserFromDB(id);
const posts = await getPostsByUser(user.id);
return { user, posts };
} catch (err) {
console.error('Fetch failed:', err);
throw err;
}
}
Асинхронные операции в цикле
Callback (невозможно читать):
const results = [];
let processed = 0;
userIds.forEach(id => {
getUser(id, (err, user) => {
if (err) {
// Обработка ошибок...
return;
}
results.push(user);
processed++;
if (processed === userIds.length) {
callback(null, results);
}
});
});
Promise:
const promises = userIds.map(id => getUser(id));
Promise.all(promises)
.then(users => {
callback(null, users);
})
.catch(err => {
callback(err);
});
async/await (ИДЕАЛЬНО):
async function getUsers() {
try {
const users = await Promise.all(
userIds.map(id => getUser(id))
);
return users;
} catch (err) {
console.error('Failed:', err);
throw err;
}
}
Основные преимущества async/await
- Синхронный стиль — код выглядит как обычный синхронный
- try/catch — знакомая обработка ошибок
- Debugger friendly — легче отлаживать
- Меньше кода — не нужны .then() цепочки
- Понятнее логика — новичкам проще понять
- Переменные — можно использовать переменные между await'ами
Важные моменты
// ✓ async/await это синтаксический сахар над Promise
const x = await promise;
// Эквивалентно
const x = await promise.then(result => result);
// ✓ Ошибка в async функции становится rejected Promise
async function test() {
throw new Error('Oops');
}
test().catch(err => console.error(err));
// ✗ Не забывайте await
async function bad() {
getUser(id); // ✗ Promise не ждём
}
async function good() {
await getUser(id); // ✓ Ждём результат
}
Вывод
- Callbacks — исторически первый подход, очень запутанный
- Promises — улучшение, но всё ещё нужны .then() цепочки
- async/await — современный стандарт, почти синхронный код
В 2025 году используй async/await везде (если браузер поддерживает, а Node.js 10+ поддерживает 100%).