Skip to main content

Функція queueMicrotask()

queueMicrotask(callback) ставить передану функцію в чергу мікротасок, і вона виконається одразу після завершення поточного синхронного коду, але раніше за будь-яку макротаску. Це найпростіший спосіб відкласти дію «на найближчий момент, коли стек спорожніє», не створюючи для цього проміс.

Теорія

TL;DR

  • queueMicrotask(fn) додає fn у Microtask Queue і повертає undefined.
  • Мікротаска виконується після того, як спорожніє Call Stack, але до першої макротаски (setTimeout, setInterval, події, I/O).
  • Поведінка ідентична до Promise.resolve().then(fn), лише без витрат на створення об'єкта Promise.
  • Event Loop вичерпує чергу мікротасок повністю, разом із тими, що додалися під час її обробки.
  • Нескінченне додавання мікротасок блокує Event Loop: жодна макротаска не дочекається своєї черги.
  • Стандарт з ECMAScript 2019, підтримується всіма сучасними браузерами та Node.js.

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

javascript
console.log('A'); queueMicrotask(() => console.log('B')); console.log('C'); // Output: // A // C // B

Спершу виконується весь синхронний код (A, потім C). Далі Event Loop бачить, що стек порожній, і виконує мікротаску (B), перш ніж переходити до макротасок.

Що саме робить queueMicrotask()

Сигнатура гранично проста:

javascript
queueMicrotask(callback);

Функція робить дві речі:

  • додає callback у чергу мікротасок;
  • гарантує, що він виконається після завершення поточної функції, але до будь-якого setTimeout, setInterval, події чи іншої макротаски.

Під капотом порядок такий:

  1. Поточний код у Call Stack виконується до кінця.
  2. Після його завершення Event Loop перевіряє чергу мікротасок.
  3. Усі мікротаски з черги виконуються по порядку, одна за одною.
  4. Тільки після цього береться наступна макротаска (наприклад, колбек setTimeout).

Порядок виконання поруч із таймером

javascript
setTimeout(() => console.log('timeout'), 0); queueMicrotask(() => console.log('microtask')); console.log('sync'); // Output: // sync // microtask // timeout

Порядок пояснюється трьома рівнями:

  1. sync, це синхронний код, він виконується першим;
  2. microtask, це черга мікротасок, вона спустошується одразу після синхронного коду;
  3. timeout, це черга макротасок, до неї Event Loop доходить в останню чергу.

Навіть setTimeout(fn, 0) ніколи не випередить мікротаску: нульова затримка означає лише «постав у чергу макротасок якнайшвидше», а не «виконай негайно».

Порівняння з Promise.then

queueMicrotask() працює так само, як Promise.resolve().then(callback), але без створення об'єкта Promise, що робить його трохи швидшим і простішим.

javascript
Promise.resolve().then(() => console.log('microtask via Promise')); queueMicrotask(() => console.log('microtask via queueMicrotask'));

Обидва колбеки потраплять у чергу мікротасок і виконаються раніше за будь-які таймери. Різниця лише в ціні: варіант із промісом додатково створює об'єкт Promise і ланцюжок then, тоді як queueMicrotask() просто кладе функцію в чергу. Якщо результат вам не потрібен і ланцюжок не будується, queueMicrotask() виражає намір точніше.

Вкладені мікротаски і ризик заблокувати Event Loop

Усі мікротаски виконуються підряд. Якщо всередині мікротаски створити нову, вона додасться в ту саму чергу і виконається до переходу до макротаски:

javascript
queueMicrotask(() => { console.log('A'); queueMicrotask(() => console.log('B')); }); // Output: // A // B

Звідси випливає небезпека: чергу мікротасок можна зробити нескінченною.

javascript
function loop() { queueMicrotask(loop); } loop();

Такий код «підвісить» Event Loop: мікротаски виконуватимуться нескінченно, і жодна макротаска (setTimeout, кліки, рендер, мережеві колбеки) не дійде до виконання. Вкладку браузера це фактично заморозить.

Підсумок

ВластивістьЗначення
Що робитьДодає функцію в чергу мікротасок
Коли виконуєтьсяПісля поточного коду, але до будь-яких макротасок
АналогPromise.resolve().then(fn)
ЧергаMicrotask Queue
Повертаєundefined
ВикористанняЛегка і швидка альтернатива промісам для мікротасок
СтандартECMAScript 2019, усі сучасні браузери та Node.js

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

  • Плутати з setTimeout(fn, 0). Таймер, це макротаска, він виконається пізніше за всі мікротаски, навіть із нульовою затримкою.
  • Думати, що мікротаска виконується «негайно». Вона все одно чекає, доки поточна функція повністю відпрацює і стек спорожніє.
  • Рекурсивно додавати мікротаски. Це не «розбиття роботи на частини», а гарантоване блокування Event Loop, бо черга мікротасок не віддає керування макротаскам.
  • Використовувати для важких обчислень. Мікротаска блокує рендер так само, як синхронний код. Для розбиття важкої роботи потрібні макротаски (setTimeout) або окремий Worker.
  • Очікувати повернене значення. queueMicrotask() завжди повертає undefined, у ньому немає ланцюжка, як у промісі, і немає способу дізнатися результат колбека.
  • Покладатися на нього для обробки помилок. Виняток усередині колбека не потрапляє в жоден catch, а спливає як необроблена помилка, тому обробляти її треба всередині самої функції.

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

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

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