Skip to main content

Ключове слово 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().

Швидкий приклад

javascript
async function myFunc() { return 42; } myFunc().then(console.log); // 42 // те саме, що: function myFunc() { return Promise.resolve(42); }

Що саме додає async

Ключове слово async перед оголошенням функції:

javascript
async function myFunc() { ... }

означає, що:

  1. Функція завжди повертає Promise.
  2. Усередині неї можна використовувати await.
  3. Повернене значення автоматично загортається в Promise.resolve(value).

Приклад 1, звичайне значення перетворюється на проміс

javascript
async function hello() { return 'Привіт!'; } hello().then(console.log);

Вивід:

text
Привіт!

Під капотом це те саме, що:

javascript
function hello() { return Promise.resolve('Привіт!'); }

Приклад 2, викидання помилки перетворюється на reject

javascript
async function fail() { throw new Error('Помилка!'); } fail().catch(err => console.error(err.message));

Вивід:

text
Помилка!

Якщо всередині async-функції стається throw, проміс автоматично відхиляється (Promise.reject).

Приклад 7, стрілкова функція. Писати можна і стрілкові async-функції:

javascript
const load = async () => { const res = await fetch('/data'); return res.json(); };

Як працює await

await можна використовувати тільки всередині async-функцій. Він призупиняє виконання функції, доки проміс не виконається.

Приклад 3, використання await

javascript
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() повертає проміс, тож його теж можна «дочекатися»:

javascript
await getData();

(тільки в async-контексті або з top-level await).

Коли інтерпретатор зустрічає await somePromise, він:

  1. призупиняє виконання поточної функції;
  2. чекає, доки проміс перейде у стан fulfilled або rejected;
  3. відновлює виконання:
    • повертаючи значення (resolve);
    • або викидаючи помилку (reject).

Приклад 4, послідовне виконання

javascript
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();

Вивід:

text
1 (через 1 секунду) 2 (через 1 секунду) 3

Завдяки await код виглядає як синхронний, але насправді він неблокуючий: JS продовжує виконувати інші задачі в цей час.

Послідовно чи паралельно

Якщо не ставити await одразу, проміси почнуть виконуватися паралельно.

Приклад 5, паралельне виконання промісів

javascript
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

javascript
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.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.