Розуміння циклу подій JavaScript: Всеосяжний посібник для фронтенд-розробників

pic

Цикл подій JavaScript є одним з найосновніших, але водночас найбільш непорозумілих концептів у фронтенд-розробці. Це основа того, як JavaScript обробляє асинхронні операції, забезпечуючи неблокуючу поведінку в однопоточному середовищі. Цей посібник розглядає тонкощі циклу подій, його складові та те, як розуміння цього механізму може допомогти розробникам писати ефективний і безпомилковий код.

Основні компоненти циклу подій

  1. Стек викликів (Call Stack): Стек викликів — це структура даних, що зберігає відомості про викликані функції. Функції додаються до стека під час виклику і видаляються після завершення виконання. Оскільки JavaScript однопоточний, лише одна функція може виконуватися в будь-який момент часу.
  2. Web API: Браузерні API, такі як setTimeout, маніпулювання DOM, і fetch, працюють асинхронно і передають завдання в цикл подій для виконання після завершення.
  3. Черга мікротасків (Microtask Queue): Обробляє задачі високого пріоритету, такі як вирішені Promises і MutationObserver. Ці задачі обробляються перед чергою макротасків.
  4. Черга макротасків (Macrotask Queue): Містить задачі типу setTimeout, setInterval та обробники подій (Event Listener). Ці задачі обробляються після очищення черги мікротасків.
  5. Цикл подій (Event Loop): Цикл подій — це оркестратор, який забезпечує виконання задач у правильному порядку. Він перевіряє стек викликів і черги завдань, переміщаючи задачі з черг у стек, коли він порожній.

Як це працює

Цикл подій постійно моніторить стек викликів. Якщо стек порожній, він спочатку обробляє задачі з черги мікротасків. Як тільки черга мікротасків порожня, він переходить до черги макротасків.

console.log("Script start");  

setTimeout(() => {  
 console.log("Macrotask: setTimeout");  
}, 0);  

Promise.resolve().then(() => {  
 console.log("Microtask: Promise");  
});  

console.log("Script end");

Покрокове виконання

  1. Стек викликів (Call Stack): Спочатку виконується console.log("Script start"), виводячи "Script start".
  2. Черга макротасків (Macrotask Queue): Колбек setTimeout додається до черги макротасків.
  3. Черга мікротасків (Microtask Queue): Розв'язка Promise додає свій колбек .then до черги мікротасків.
  4. Стек викликів (Call Stack): Виконується console.log("Script end"), виводячи "Script end".
  5. Виконання мікротасків (Microtasks Executed): Черга мікротасків обробляє колбек Promise, виводячи "Microtask: Promise".
  6. Виконання макротасків (Macrotasks Executed): Нарешті обробляється колбек setTimeout з черги макротасків, виводячи "Macrotask: setTimeout".

pic

Більш складний сценарій

Давайте додамо вкладені мікротаски та макротаски, щоб побачити їхню взаємодію:

console.log("Start");  

setTimeout(() => {  
 console.log("Macrotask 1: setTimeout");  
 Promise.resolve().then(() => {  
 console.log("Microtask 1: Nested Promise");  
 });  
}, 0);  

Promise.resolve().then(() => {  
 console.log("Microtask 2: Promise");  
});  

console.log("End");

Покрокове виконання

  1. Стек викликів (Call Stack): console.log("Start") виконується, виводячи "Start".
  2. Черга макротасків (Macrotask Queue): Колбек setTimeout додається до черги макротасків.
  3. Черга мікротасків (Microtask Queue): Перше виконання Promise додає свій колбек .then до черги мікротасків.
  4. Стек викликів (Call Stack): console.log("End") виконується, виводячи "End".
  5. Виконання мікротасків (Microtasks Executed): Перший мікротаск виконується, виводячи “Microtask 2: Promise”.
  6. Виконання макротасків (Macrotasks Executed): Колбек setTimeout обробляється, виводячи "Macrotask 1: setTimeout".
    Всередині цього макротаска додається новий мікротаск (“Microtask 1: Nested Promise”) до черги мікротасків.
    7.
    Виконання вкладених мікротасків (Nested Microtasks Executed): Вкладений мікротаск виконується, виводячи “Microtask 1: Nested Promise”.

pic

виведення

Поширені непорозуміння

  • Нульова затримка в setTimeout: Встановлення setTimeout з затримкою 0 не означає, що колбек виконається негайно. Він додається до черги макротасків і виконується після очищення поточного стека викликів та всіх мікротасків.
  • Блокуючий код: Довготривалі синхронні операції блокують стек викликів, перешкоджаючи виконанню інших задач. Розуміння цієї поведінки допомагає уникнути зависання інтерфейсу.

Цикл подій в React.js

В React цикл подій є важливим для:

  • Оновлення стану (State Updates): Забезпечення пакетного оновлення та асинхронного застосування змін.
  • Рендеринг (Rendering): Оптимізація циклів рендерингу за допомогою requestAnimationFrame.
  • Ефекти (Effects): Ефективне управління useEffect та функціями очищення.

Налагодження циклу подій

Інструменти, такі як Chrome DevTools, дозволяють розробникам аналізувати цикл подій. Вкладка Performance надає хронологічний вигляд завдань, мікротасків і подій рендерингу. Використовуйте цей інструмент для виявлення вузьких місць і оптимізації продуктивності.

Поради з оптимізації продуктивності

  1. Розбивайте довгі завдання: Використовуйте requestIdleCallback або розділяйте завдання на менші частини, щоб уникнути блокування інтерфейсу.
  2. Зменшуйте повторні обчислення (Reflows): Уникайте надмірної маніпуляції DOM у швидкій послідовності, щоб знизити накладні витрати на рендеринг.
  3. Використовуйте асинхронні API: Віддавайте перевагу Promises і async/await для більш чистого та ефективного асинхронного коду.

Висновок

Глибоке розуміння циклу подій дозволяє фронтенд-розробникам створювати додатки, які не лише функціональні, але й високо продуктивні та відгукуються швидко. Використовуючи можливості циклу подій і оптимізуючи управління завданнями, ви можете забезпечити безперервний користувацький досвід.

Джерела

Перекладено з: Understanding the JavaScript Event Loop: A Comprehensive Guide for Front-End Developers

Leave a Reply

Your email address will not be published. Required fields are marked *