Skip to main content

Чому await зупиняє лише поточну функцію

await не блокує потік виконання JavaScript, він призупиняє лише ту async-функцію, у якій стоїть. Решта програми, обробники подій, таймери й інші async-функції продовжують працювати, бо await віддає керування назад у Event Loop.

Теорія

TL;DR

  • JavaScript виконує код в одному потоці, але керує задачами через черги Event Loop.
  • await не зупиняє цей потік, він «заморожує» тільки поточну async-функцію.
  • Решту тіла функції рушій загортає в мікротаску, яка виконається після завершення промісу.
  • Стек викликів звільняється, тому інший код (таймери, події, інші функції) виконується далі.
  • Коли проміс переходить у fulfilled або rejected, функція продовжується рівно з місця await.
  • Це асинхронна пауза, а не блокування: жодних потоків ОС чи sleep тут немає.

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

javascript
async function task() { console.log('Початок'); await new Promise(r => setTimeout(r, 2000)); console.log('Кінець'); } task(); // Цей рядок виконається одразу, рушій не заблокований console.log('Цей код виконується одразу!');

Вивід:

text
Початок Цей код виконується одразу! (через 2 с) Кінець

await призупиняє тільки функцію task(), а основний код іде далі. JS не «зависає»: усередині task() просто створюється мікротаска, яка повернеться в чергу після спрацювання таймера.

Що відбувається під капотом

Коли рушій зустрічає await promise, відбувається таке:

  1. Виконання поточної async-функції призупиняється.
  2. Рушій створює мікротаску (callback), яка має продовжити функцію після завершення промісу.
  3. Керування повертається в Event Loop, стек викликів звільняється.
  4. Рушій виконує інші задачі: інші функції, обробники подій, таймери тощо.
  5. Коли проміс завершено (fulfilled або rejected), мікротаска виконується і функція відновлюється саме з того рядка, де стояв await.

Усе це реалізовано через чергу мікротасок (PromiseJobs). Ніяких додаткових потоків чи справжнього sleep рушій не використовує.

Крок за кроком в Event Loop

ЕтапЩо відбувається
1task() потрапляє в Call Stack і виконує код до await
2На await функція «заморожується», створюється мікротаска
3Стек очищується, інші скрипти продовжують виконуватися
4Коли проміс завершується, мікротаска повертається в чергу
5Після поточного тіку Event Loop виконується решта коду task()

Якби await справді блокував потік, увесь JavaScript «завис» би на ці 2 секунди: не спрацював би жоден обробник подій і жоден setTimeout. Але такого не стається: поки task() чекає, Event Loop спокійно обробляє інші події.

Паралельні async-функції не чекають одна одну

javascript
async function foo() { console.log('foo start'); await new Promise(res => setTimeout(res, 1000)); console.log('foo end'); } async function bar() { console.log('bar start'); await new Promise(res => setTimeout(res, 500)); console.log('bar end'); } foo(); bar(); console.log('main thread');

Вивід:

text
foo start bar start main thread (через 0.5 с) bar end (через 1 с) foo end

await не заважає виконанню інших функцій. Кожна функція просто «заморожується» до свого промісу, а потім продовжує роботу незалежно від сусідньої.

Мікротаска відновлення і порядок виводу

javascript
async function demo() { console.log('A'); await Promise.resolve(); console.log('B'); } demo(); console.log('C');

Результат:

text
A C B

Чому саме так:

  • A виконується синхронно, до await;
  • await зупиняє функцію і ставить її продовження (B) у чергу мікротасок;
  • основний код доходить до C;
  • і лише коли стек порожній, рушій виконує мікротаски, тобто B.

Проста аналогія: async-функція, це серіал. На await конкретний серіал ставиться на паузу, але інші канали продовжують мовлення. Коли потрібний проміс готовий, серія йде далі рівно з того кадру, на якому зупинилася.

Що await робить і чого не робить

Що робить awaitЧого не робить
Призупиняє лише поточну async-функціюНе блокує потік JavaScript
Повертає керування в Event LoopНе заважає іншим задачам виконуватися
Після завершення промісу відновлює виконанняНе «заморожує» весь застосунок
Реалізований через мікротаски (PromiseJobs)Не використовує потоки ОС чи sleep

Типові помилки

  • Вважати, що await зупиняє всю сторінку, і через це уникати його в коді, який реагує на події.
  • Плутати паузу функції з блокуванням: справжнє блокування дає синхронний цикл while (Date.now() < end) {}, і саме він «вішає» вкладку, бо не віддає стек.
  • Ставити await у циклі там, де запити незалежні: функція чекає їх послідовно, хоча можна запустити все разом через Promise.all().
  • Очікувати, що два await підряд виконаються паралельно. Вони строго послідовні: другий стартує лише після завершення першого.
  • Забувати, що після await код продовжується вже в мікротасці, тому порядок логів відрізняється від синхронного читання зверху вниз.

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

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

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