Нещодавно я спілкувався з другом, який також розробник, і, як завжди, ми знову потрапили в технічну дискусію. Ми часто обговорюємо такі питання, щоб залишатися у формі, допомагати один одному розв'язувати проблеми, з якими стикаємось в процесі розробки, і навіть інколи кидаємо один одному виклики.
Цього разу він запитав мене:
"Коли слід використовувати Promise замість async/await?"
Я вже стикався з цією плутаниною, тому відповідь здалася мені досить очевидною. Однак наступного дня в офісі я випадково поставив це питання колезі — і на моє здивування, він відповів неправильно.
Тоді я зрозумів:
Навіть досвідчені розробники можуть заплутатися між Promises і async/await, особливо під час інтерв'ю.
Тому я вирішив написати про це: чітко, практично і з реальними прикладами. Адже ця проста різниця може стати пасткою, якщо не бути уважним.
🚀 Спершу, у чому різниця?
Давайте почнемо з простого.
👉 Promise — це:
Об'єкт, який представляє асинхронну операцію, яка ще не завершена, але в кінцевому підсумку поверне результат або помилку.
👉 async/await
— це:
Просто синтаксичний цукор, побудований на основі Promises.
💡 Простий приклад з реального життя
Уявімо, що ви отримуєте користувача, а потім — його пости за допомогою ID.
З використанням Promises:
getUser()
.then(user => {
return getPostsByUser(user.id);
})
.then(posts => {
console.log("Пости:", posts);
})
.catch(err => {
console.error("Помилка:", err);
});
З використанням async/await:
async function showUserAndPosts() {
try {
const user = await getUser();
const posts = await getPostsByUser(user.id);
console.log("Пости:", posts);
} catch (err) {
console.error("Помилка:", err);
}
}
🧠 То… що ж вибрати?
Ось що я дізнався з реального досвіду (не з підручників):
❗ async/await не працює, як очікується, з Array.map()
або forEach()
Це одна з найбільш поширених помилок, в яку я сам неодноразово потрапляв (і інколи ще роблю це).
Уявіть, що ви намагаєтесь використовувати await
всередині Array.map()
:
const ids = [1, 2, 3];
const results = ids.map(id => {
const data = await fetchById(id); // ❌ Це не спрацює
return data;
});
console.log(results);
На перший погляд, здається, що це має працювати — адже ви використовуєте await
в середині map
, правда? Але це викличе помилку:
await можна використовувати тільки в асинхронних функціях або на верхньому рівні модулів
Навіть якщо зробити зворотний виклик async
:
const results = ids.map(async id => {
return await fetchById(id);
});
console.log(results); // ❌ Все одно неправильно
Тепер це не викличе аварії, але results
буде масивом незавершених Promises, а не реальними даними, які ви очікуєте.
✅ Правильний спосіб: використовуйте Promise.all()
з async/await
Щоб отримати результати, потрібно зібрати всі Promises, а потім чекати, поки вони завершаться:
const ids = [1, 2, 3];
const promises = ids.map(id => fetchById(id));
const results = await Promise.all(promises);
console.log(results); // ✅ Тепер ви отримаєте виконані дані
Це правильний спосіб обробки асинхронних операцій всередині map
, forEach
або подібних методів.
🧵 Хочете послідовне виконання? Використовуйте for...of
з await
Якщо ви хочете отримувати дані одне за одним у порядку, використовуйте простий цикл for...of
:
const ids = [1, 2, 3];
const results = [];
for (const id of ids) {
const data = await fetchById(id);
results.push(data);
}
Це корисно, коли важливий порядок або коли ви не хочете завантажувати всі запити одночасно.
⚠️ async/await не працює на верхньому рівні за замовчуванням
Інша поширена помилка — спроба використати await
поза асинхронною функцією:
const results = await fetchData(); // ❌ SyntaxError
Щоб виправити це, обгорніть у функцію async
:
async function getData() {
const results = await fetchData();
console.log(results);
}
Або використовуйте await на верхньому рівні, який підтримується в сучасних ES модулях та інструментах, таких як Vite, Next.js або нові версії Node:
const results = await fetchData(); // ✅ Top-level await
console.log(results);
🎯 Відповідь для інтерв'ю
Ось що я відповідаю, коли мене запитують про це:
“Я віддаю перевагу
async/await
для робочих процесів з кількома етапами, оскільки це читається чисто і легко підтримується. Але коли використовую такі методи, якmap
абоforEach
, я переключаюсь на використання Promises зPromise.all()
— тому щоawait
не працює, як очікується, всередині них. А для утиліт або коли ланцюгування кращий, я залишаюсь на звичайних Promises.”
✍️ Підсумки
Це було не просто ще одне "технічне питання" — це виникло з реальних розмов з колегами та підготовки до інтерв'ю. Основний висновок?
- І Promises, і async/await чудові — все залежить від контексту.
- Будьте обережні з циклами і
Array.map()
, коли використовуєтеawait
. Це не працюватиме, якщо ви не правильно обробляєте Promises. - Практикуйте перемикання між обома стилями.
Перекладено з: When Should You Use Promises Instead of async/await? (Sometimes Developers Get This Wrong)