За что отвечает Event Loop кроме micro и macro tasks
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Event Loop: полные обязанности кроме микро и макро задач
Event Loop - это намного больше чем просто управление очередями задач. Он отвечает за координацию всего асинхронного выполнения кода в JavaScript, и его обязанности гораздо шире, чем микротаски и макротаски.
Основная структура Event Loop
Event Loop постоянно выполняет следующий цикл:
while (eventLoop.waitForTask()) {
// 1. Выполнить одну макротаску
const macrotask = macrotaskQueue.pop();
if (macrotask) execute(macrotask);
// 2. Выполнить все микротаски
while (microtaskQueue.hasTasks()) {
const microtask = microtaskQueue.pop();
execute(microtask);
}
// 3. Проверить нужна ли перерисовка
if (isRepaintTime()) {
applyStyles();
layout();
paint();
}
// 4. Выполнить requestAnimationFrame callbacks
executeAnimationFrameCallbacks();
// 5. Обработать другие события
handleUserInteractions();
handleIntersectionObservers();
handleResizeObservers();
// и многое другое...
}
1. Управление Call Stack (стеком вызовов)
Event Loop управляет самым главным - стеком вызовов:
function a() {
console.log('a');
b();
}
function b() {
console.log('b');
c();
}
function c() {
console.log('c');
}
a();
// Event Loop управляет этим стеком: a() -> b() -> c() -> pop -> pop -> pop
Event Loop отвечает за:
- Выполнение функций из Call Stack
- Выполнение глобального контекста
- Синхронный код (который идёт первым)
2. Управление Callback Queue (очередь callback'ов)
Это разные очереди для разных типов callback'ов:
// Макротаски (Macrotask Queue):
- setTimeout
- setInterval
- setImmediate (Node.js)
- I/O operations
- UI events
- requestIdleCallback
// Микротаски (Microtask Queue):
- Promise.then/catch/finally
- MutationObserver
- process.nextTick (Node.js)
- queueMicrotask()
- Object.observe (deprecated)
// Другие очереди:
- requestAnimationFrame (rAF Queue)
- Render Queue
Пример различных очередей:
console.log('1 - синхронно');
setTimeout(() => console.log('2 - макротаска'), 0);
Promise.resolve().then(() => console.log('3 - микротаска'));
requestAnimationFrame(() => console.log('4 - animation frame'));
queueMicrotask(() => console.log('5 - микротаска'));
console.log('6 - синхронно');
// Порядок выполнения:
// 1 - синхронно
// 6 - синхронно
// 3 - микротаска
// 5 - микротаска
// 4 - animation frame
// 2 - макротаска
3. Управление Rendering (перерисовка страницы)
Event Loop определяет КОГДА браузер перерисует страницу:
// Инициирует перерисовку
const element = document.querySelector('.box');
element.style.width = '100px'; // Изменение DOM
// Синхронно ещё виден старый размер
console.log(element.offsetWidth); // может быть 50px
// Event Loop решает перерисовать
// Браузер:
// 1. Выполняет все микротаски
// 2. Вычисляет style changes
// 3. Layout (reflow)
// 4. Paint (repaint)
// 5. Composite
Пример оптимизации рендеринга через Event Loop знание:
// ПЛОХО - вызывает много reflows
for (let i = 0; i < 1000; i++) {
element.style.width = (i * 10) + 'px';
console.log(element.offsetWidth); // Заставляет браузер пересчитывать layout
}
// ХОРОШО - батчим изменения
for (let i = 0; i < 1000; i++) {
element.style.width = (i * 10) + 'px';
}
// Event Loop сам решит когда перерисовать
// ОТЛИЧНОЕ - использование requestAnimationFrame
requestAnimationFrame(() => {
element.style.width = '100px';
// Гарантирует выполнение ПЕРЕД следующей перерисовкой
});
4. requestAnimationFrame (rAF)
Это специальная очередь, которая выполняется БЕ перед perеприсовкой, не в основной очереди:
console.log('1');
setTimeout(() => console.log('2 - макротаска'), 0);
requestAnimationFrame(() => console.log('3 - перед перерисовкой'));
Promise.resolve().then(() => console.log('4 - микротаска'));
console.log('5');
// Выполнение:
// 1
// 5
// 4 - микротаска
// 3 - requestAnimationFrame (перед перерисовкой)
// 2 - макротаска
Практический пример анимации:
function animateElement(element, duration = 1000) {
const startTime = performance.now();
function animate(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
element.style.opacity = progress;
element.style.transform = `translateX(${progress * 100}px)`;
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
// Event Loop гарантирует, что animate вызовется прямо перед перерисовкой
}
5. MutationObserver
Event Loop отвечает за когда вызывать MutationObserver callbacks:
const observer = new MutationObserver((mutations) => {
console.log('Mutations detected:', mutations);
});
observer.observe(document.body, {
attributes: true,
childList: true,
subtree: true
});
// Event Loop вызовет MutationObserver callback после всех микротасок
// но ПЕРЕД следующей макротаской
document.body.appendChild(document.createElement('div'));
Promise.resolve().then(() => console.log('После микротаски, но MutationObserver уже вызвался'));
6. IntersectionObserver
Отвечает за отслеживание видимости элементов в viewport:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log('Element is visible');
// Event Loop вызывает это после перерисовки
}
});
});
observer.observe(element);
// Event Loop проверяет видимость и вызывает callback
// обычно после requestAnimationFrame и перед следующей макротаской
7. ResizeObserver
Отслеживает изменения размера элементов:
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach(entry => {
console.log('Element resized:', entry.contentRect);
// Event Loop вызывает callback когда размер изменился
});
});
resizeObserver.observe(element);
// Event Loop сам определит когда это нужно вызвать
8. Обработка User Interactions (события)
Event Loop управляет когда обрабатываются события:
button.addEventListener('click', () => {
console.log('Click handler');
// Event Loop помещает это в очередь событий
});
// Порядок обработки события:
// 1. User clicks button
// 2. Event Loop берёт событие
// 3. Выполняет все listeners (синхронно)
// 4. Выполняет все микротаски
// 5. Может перерисовать
// 6. Переходит к следующему событию
9. Управление Memory (сборка мусора)
Event Loop взаимодействует с garbage collector:
// Event Loop определяет когда можно запустить сборку мусора
// Обычно это происходит когда:
// - Нет текущих задач
// - Нет очередей callback'ов
// - Браузер имеет "свободное время"
const largeObject = { /* ... */ };
// После удаления ссылки
// Event Loop и GC решат когда освободить память
10. Обработка Web Workers
Event Loop координирует communication с Web Workers:
const worker = new Worker('worker.js');
worker.postMessage({ data: 'test' });
worker.onmessage = (event) => {
console.log('Message from worker:', event.data);
// Event Loop вызывает это когда пришло сообщение
};
// Event Loop:
// - Отправляет сообщение в Worker thread
// - Слушает ответ
// - Вызывает callback в основном потоке
11. Network Request Handling
Event Loop отвечает за completion callbacks fetch/XHR:
fetch('/api/data')
.then(response => {
console.log('Response received');
// Event Loop вызовет это когда пришёл ответ
// Это микротаска
})
.catch(error => {
// Event Loop вызовет это при ошибке
// Также микротаска
});
// Event Loop:
// - Инициирует сетевой запрос
// - Слушает завершение
// - Помещает callback в микротаски
12. IndexedDB и Storage API
Event Loop управляет callback'ами для асинхронных операций с БД:
const request = indexedDB.open('myDatabase');
request.onsuccess = (event) => {
// Event Loop вызывает это когда БД открыта
};
request.onerror = (event) => {
// Event Loop вызывает это при ошибке
};
Полная диаграмма Event Loop
┌─────────────────────────────────────────────────────────┐
│ Event Loop Cycle │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 1. Выполнить одну макротаску (setTimeout, setInterval) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 2. Выполнить ВСЕ микротаски (Promise, queueMicrotask) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 3. Вычислить стили и выполнить layout (reflow) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 4. Выполнить requestAnimationFrame callbacks │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 5. Перерисовать страницу (paint + composite) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 6. Обработать User Events (click, scroll, etc.) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 7. Проверить Observers (Intersection, Resize, etc.) │
└─────────────────────────────────────────────────────────┘
↓
(повторять)
Event Loop - это огромно сложный механизм, который синхронизирует все аспекты выполнения JavaScript в браузере: синхронный код, асинхронные операции, рендеринг, события, и наблюдатели. Понимание всех его аспектов критично для написания производительного и надёжного кода.