Ключове слово async
Ключове слово async перед оголошенням функції означає, що ця функція завжди повертає Promise, а всередині неї можна використовувати await. Повернене значення автоматично загортається в Promise.resolve(value), тому виклик такої функції завжди можна продовжити через .then() або await.
Теорія
TL;DR
async function myFunc() { ... }завжди повертаєPromise, навіть якщо в тілі немає жодного проміса.return valueвсередині стаєPromise.resolve(value).throw errorвсередині стаєPromise.reject(error).awaitдозволено тільки всерединіasync-функцій (або як top-level await у модулі).awaitпризупиняє саму функцію, але не блокує потік: JS у цей час виконує інші задачі.- Помилки зручно ловити через
try...catchзамість.catch().
Швидкий приклад
async function myFunc() {
return 42;
}
myFunc().then(console.log); // 42
// те саме, що: function myFunc() { return Promise.resolve(42); }Що саме додає async
Ключове слово async перед оголошенням функції:
async function myFunc() { ... }означає, що:
- Функція завжди повертає
Promise. - Усередині неї можна використовувати
await. - Повернене значення автоматично загортається в
Promise.resolve(value).
Приклад 1, звичайне значення перетворюється на проміс
async function hello() {
return 'Привіт!';
}
hello().then(console.log);Вивід:
Привіт!Під капотом це те саме, що:
function hello() {
return Promise.resolve('Привіт!');
}Приклад 2, викидання помилки перетворюється на reject
async function fail() {
throw new Error('Помилка!');
}
fail().catch(err => console.error(err.message));Вивід:
Помилка!Якщо всередині async-функції стається throw, проміс автоматично відхиляється (Promise.reject).
Приклад 7, стрілкова функція. Писати можна і стрілкові async-функції:
const load = async () => {
const res = await fetch('/data');
return res.json();
};Як працює await
await можна використовувати тільки всередині async-функцій. Він призупиняє виконання функції, доки проміс не виконається.
Приклад 3, використання await
async function getData() {
console.log('Завантажуємо...');
const res = await fetch('https://api.github.com/users/octocat');
const user = await res.json();
console.log('Готово:', user.login);
}
getData();При цьому сам виклик getData() повертає проміс, тож його теж можна «дочекатися»:
await getData();(тільки в async-контексті або з top-level await).
Коли інтерпретатор зустрічає await somePromise, він:
- призупиняє виконання поточної функції;
- чекає, доки проміс перейде у стан
fulfilledабоrejected; - відновлює виконання:
- повертаючи значення (
resolve); - або викидаючи помилку (
reject).
- повертаючи значення (
Приклад 4, послідовне виконання
async function steps() {
console.log('1');
await new Promise(res => setTimeout(res, 1000));
console.log('2');
await new Promise(res => setTimeout(res, 1000));
console.log('3');
}
steps();Вивід:
1
(через 1 секунду)
2
(через 1 секунду)
3Завдяки await код виглядає як синхронний, але насправді він неблокуючий: JS продовжує виконувати інші задачі в цей час.
Послідовно чи паралельно
Якщо не ставити await одразу, проміси почнуть виконуватися паралельно.
Приклад 5, паралельне виконання промісів
async function parallel() {
const p1 = fetch('/api/user');
const p2 = fetch('/api/posts');
const [user, posts] = await Promise.all([p1, p2]);
console.log('Обидві відповіді отримано');
}Так ви не чекаєте кожен запит окремо, а обидва виконуються одночасно. Послідовний варіант (await fetch(...) двічі поспіль) відпрацює приблизно за суму часів, паралельний, за час найповільнішого запиту.
Обробка помилок
В async-функціях помилки зручно ловити через try/catch замість .catch().
Приклад 6, комбінування з try...catch
async function loadData() {
try {
const res = await fetch('/bad-url');
const data = await res.json();
console.log('OK:', data);
} catch (err) {
console.error('Помилка:', err.message);
}
}try...catch ловить і синхронні винятки в тілі функції, і відхилені проміси, які ви дочекалися через await.
Підсумок
Уявіть, що
async- це обіцянка, аawait- це пауза, доки обіцянку не буде виконано. Тобтоasyncробить функцію такою, що «дає обіцянку», аawaitдозволяє «дочекатися» результату цієї обіцянки, але без блокування інших процесів.
| Особливість | Що робить |
|---|---|
async | робить функцію такою, що повертає Promise |
await | «зупиняє» виконання до завершення проміса |
return value | перетворюється на Promise.resolve(value) |
throw error | перетворюється на Promise.reject(error) |
| Помилки | можна ловити через try...catch |
| Використання | тільки всередині async (або top-level await) |
Типові помилки
- Думати, що
asyncприскорює код. Він лише змінює тип результату наPromise, обчислення від цього не стають паралельними. awaitу циклі для незалежних запитів. Такий код стає послідовним; для незалежних операцій потрібенPromise.all.- Викликати
async-функцію і не обробляти її проміс. Помилка всередині перетвориться на unhandled rejection, і ви її не побачите. - Ставити
awaitпозаasync-функцією у звичайному скрипті: це SyntaxError. Top-level await працює лише в модулях. - Забувати, що
returnвсерединіtryзawaitвідрізняється відreturn promise. Безawaitпомилку проміса не спіймає локальнийcatch.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.