await у стрілкових функціях
Так, await у стрілковій функції використовувати можна, але лише тоді, коли стрілка оголошена з ключовим словом async. Сама форма запису функції тут ні до чого: дозвіл на await дає async, а не стрілка чи function.
Теорія
TL;DR
awaitдозволений лише всерединіasync-функції або на верхньому рівні ES-модуля.- Стрілкова функція стає асинхронною так само просто:
async () => { ... }. - Без
asyncцеSyntaxError, код не виконається взагалі, бо помилка виникає ще під час парсингу. - Будь-яка
async-функція, зокрема стрілкова, завжди повертає проміс. async-стрілку можна передавати як колбек, наприклад у.then(), іawaitусередині неї цілком легальний.- Поза функцією
awaitпрацює тільки як top-level await в ESM.
Швидкий приклад
const fetchData = async () => {
const data = await Promise.resolve('Готово');
console.log(data);
};
fetchData();Результат:
Готовоasync перед стрілковою функцією перетворює її на асинхронну функцію, яка завжди повертає проміс.
Де взагалі дозволений await
await, це оператор, який можна вживати тільки в двох місцях:
- усередині функції, оголошеної з
async; - на верхньому рівні модуля (top-level await).
Якщо використати await поза async, буде синтаксична помилка:
// Помилка
const result = await Promise.resolve(42);
// SyntaxError: await is only valid in async functionsТе саме зі звичайною стрілкою без async:
const foo = () => {
const data = await Promise.resolve('value');
console.log(data);
};
// SyntaxError: await is only valid in async functionsЗверніть увагу: це не помилка часу виконання. JS навіть не почне виконувати код, бо await неприпустимий у звичайній функції, і скрипт падає ще на етапі розбору.
Що повертає async-стрілка
async-стрілка завжди повертає проміс, навіть якщо в ній немає жодного await:
const sum = async (a, b) => a + b;
console.log(sum(1, 2)); // Promise { 3 }Це те саме, що написати:
function sum(a, b) {
return Promise.resolve(a + b);
}Тому результат такої функції треба або await-ити, або обробляти через .then().
Різні форми запису
Стрілка з await у звичайному вигляді:
const loadUser = async () => {
const res = await fetch('/api/user');
return res.json();
};З параметрами:
const getUser = async (id) => {
const res = await fetch(`/api/user/${id}`);
return res.json();
};Багаторядкова форма:
const processData = async () => {
const data = await fetch('/api/data');
const json = await data.json();
console.log(json);
};У ланцюжку промісів:
Promise.resolve('A')
.then(async (x) => {
const y = await Promise.resolve('B');
return x + y;
})
.then(console.log); // "AB"Тут then() приймає async-стрілкову функцію, і await усередині неї повністю легальний: then просто дочекається промісу, який ця функція поверне.
Top-level await
Сучасні JS-модулі (ESM) дозволяють використовувати await поза функціями, якщо код виконується саме як модуль, а не як звичайний <script> без type="module":
// main.js
const data = await fetch('/api/data').then(r => r.json());
console.log(data);Це і є top-level await. Він працює лише в ESM (у браузері з <script type="module"> або в Node.js з "type": "module" у package.json). У CommonJS-модулі такий код не запрацює.
Проста аналогія:
asyncперед стрілковою функцією, це перепустка, яка дає право вживатиawaitусередині. Без неї рушій просто не знає, куди повертати результат очікування.
Підсумкова таблиця
| Питання | Відповідь |
|---|---|
Чи можна вживати await у стрілковій функції? | Так, якщо вона оголошена як async |
Що буде без async? | SyntaxError: await is only valid in async functions |
Що повертає async-стрілка? | Завжди Promise |
Чи можна await у .then() або колбеках? | Так, якщо колбек оголошений як async |
Чи можна await поза функцією? | Тільки як top-level await у модулях |
Типові помилки
- Ставити
awaitу звичайній стрілці й дивуватися, що не працює навіть код до нього: це помилка парсингу, а не виконання. - Забувати, що
async-стрілка повертає проміс, і писатиconst x = sum(1, 2), отримуючиPromise, а не число. - Писати
asyncна зовнішній функції, аawaitвикористовувати всередині вкладеного звичайного колбека, наприкладarr.forEach(item => { await save(item); }). Помічати треба саме той колбек:async item => .... - Чекати, що
arr.forEach(async ...)дочекається всіх операцій:forEachігнорує повернені проміси, потрібенfor...ofзawaitабоPromise.all(arr.map(async ...)). - Використовувати top-level await у файлі, який завантажується як звичайний скрипт або як CommonJS: там він недоступний.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.