Для сотрудничестваarrow
Telegram

Event loop

render queue

Event loop - механизм обработки цикла событий в браузере. JavaScript синхронный однопоточный язык программирования, в котором может выполнятся только одна операция.
Мы можем описать event loop как цикл, который проверяет, есть ли у нас задачи в ожидании

Основные источники задач Event loop:

Выполнение кода в глобальной области видимости (Global Execution Context):

<script>

Обработка событий (Event Handlers):

addEventListner
DOMMutationObserver
IntersectionObserver
RequestAnimationFrame

Сетевые запросы (XHR, Fetch API):

XmlHttpRequest
fetch

Таймеры (Timers):

setTimeout
setInterval

Дополнительно:

requestIdleCallback

Давайте рассмотрим подробнее каждый компонент на рисунке

Task queue

task queue
1console.log('Task 1');
2setTimeout(()=> console.log('task N'));
3console.log('Task 2');

Как вы заметили во коде имеется setTimeout когда интерпретатор доходит до данной строки он отправлять задачу в список Web APIs через Event Loop
Когда вызывается функция setTimeout, она ставит таймер на выполнение кода через определенный промежуток времени. Этот промежуток времени измеряется в миллисекундах. По окончанию которого задача переходит в Task queue

Что такое Web APIs? И что входит в Web APIs?

web APIs

Web APIs предоставляют асинхронные функции и методы, которые используются в веб-приложениях. Когда выполняется асинхронная операция, она уходит из основного потока выполнения и передается в Web APIs. Затем, по завершении этой асинхронной операции, соответствующая задача помещается в очередь задач

  • XMLHttpRequest и Fetch API: Предоставляют возможность совершать сетевые запросы (HTTP) асинхронно.
  • setTimeout и setInterval: Позволяют планировать выполнение кода через определенный промежуток времени.
  • DOM (Document Object Model) API: Предоставляет интерфейс для взаимодействия с элементами и структурой документа, а также реагирования на события.
  • WebSockets API: Позволяет создавать постоянные двусторонние соединения между клиентом и сервером.
  • Geolocation API: Предоставляет информацию о местоположении устройства.
  • Web Workers API: Позволяет создавать рабочие потоки для параллельного выполнения задач.
  • IndexedDB API: Предоставляет доступ к базе данных на клиентской стороне.
  • Web Audio API: Позволяет создавать и манипулировать звуками в веб-приложениях.
  • Web Animation API :Предоставляет возможность управлять анимациями на веб-странице.
  • Notification API: Позволяет веб-приложениям отправлять уведомления пользователю.
  • Battery Status API: Предоставляет информацию о состоянии заряда аккумулятора устройства.
  • Clipboard API: Позволяет взаимодействовать с буфером обмена (копирование и вставка).

Microtask queue

microtask queue

Давайте теперь рассмотрим Microtask queue. Она состоит из promise callbackиMutationObserver
Возьмем наш фрагмент кода и добавим в него Microtask

1console.log('Task 1');
2setTimeout(()=> console.log('task N'));
3Promise.resolve('Task 3').then(val=>console.log(val))
4console.log('Task 2');

Call stack

Call Stack используется для управления текущими вызовами функций и их выполнением.
Основные особенности стека вызовов:

  • Last In, First Out (LIFO): Это означает, что последняя функция, добавленная в стек вызовов, будет первой, которая будет выполнена и удалена из стека.
  • Хранение контекста выполнения: Для каждого вызова функции в стеке вызовов сохраняется контекст выполнения, включая локальные переменные, параметры функции и адрес возврата (то место, куда вернется управление после завершения функции).
  • Определение места в программе: Стек вызовов также позволяет определить, где программа находится в определенный момент времени. Если стек вызовов не пуст, то это указывает на то, что программа находится внутри выполнения какой-то функции.
  • Когда вы вызываете функцию в JavaScript, её контекст выполнения (вместе с локальными переменными и параметрами) помещается в стек вызовов. Когда функция завершает свое выполнение, её контекст удаляется из стека. Этот процесс повторяется при вызове других функций.
1function scrollIntoView () {
2  debugger;
3  console.log('No internet access');
4}
5
6function lookWeatherWidget() {
7  scrollIntoView();  
8}
9function getPin(num) {
10    return 'pin '.concat(num) 
11}
12function getWeatherForecast() {
13    console.log('Take the phone Unblock');
14    const pin = [];
15  while (pin.length < 4) {
16    pin.push(getPin(pin.length));
17  }
18    console.log(pin);
19  lookWeatherWidget();
20}
21console.log(getWeatherForecast());
call stack

Из чего состоит Render queue? И как он работает?

render queue

В целом, рендеринг веб-страницы в браузере управляется браузерным движком рендеринга, который отвечает за перерисовку элементов на экране в соответствии с изменениями в DOM и стилях.

Рендеринг кадров состоит из нескольких этапов:
  • RequestAnimationFrame
  • Style Recalculation
  • Layout
  • Paint
  • Composite

RequestAnimationFrame вызывается примерно каждый 16,6 мс (1000мс/60fps)+ Это означает что Render вызывается каждые 16.6 мс и приступает к выполнению перерисовки по завершению текущей задачи. Отчет до следующего рендеринга начинается по окончанию выполнения текущего.

Task queue

Style Recalculation происходит изменение в структуре DOM или CSSOM (например, добавление, удаление или изменение элемента, изменение класса элемента, изменение стилей через JavaScript), браузер должен пересчитать стили для всех элементов, которые могут быть затронуты этим изменением.

1element.style.width = '100px'; 
2element.classList.add('new-style')

Layout

Layout (формирование макета): Если изменения затрагивают геометрические свойства элементов (например, размеры или положение), браузер создает новый макет страницы.

1const layoutElement = document.querySelector('.layout-element');
2// Изменяем размеры элемента, что приведет к пересчету макета
3setTimeout(() => {
4  layoutElement.style.width = '150px';
5  layoutElement.style.height = '150px';
6}, 1000);

В этом примере: У нас есть элемент с классом .layout-element, который имеет начальные размеры 100x100 пикселей и красный фон.
Мы используем CSS-переход (transition) для плавного анимированного изменения ширины и высоты.
С помощью JavaScript мы изменяем ширину и высоту элемента черезstyle.width и style.height после задержки в 1 секунду.
Это изменение размеров элемента приведет к пересчету макета, поскольку браузер должен пересчитать, как этот элемент вписывается в контекст страницы с новыми размерами. Обратите внимание, что изменение свойств, влияющих на геометрию элемента (таких как размеры), обычно вызывает пересчет макета.

Paint

Paint (отрисовка): На этом этапе происходит создание фактического изображения на основе информации о макете. Это включает в себя определение, какие пиксели должны быть отображены на экране для каждого элемента, включая их цвет, фон, границы и другие стили.

Painting (отрисовка):
  • Фон (Background): Браузер отрисовывает фон элементов в соответствии с их стилями, включая цвет и изображения фона.
  • Текст и шрифты (Text and Fonts): Браузер рисует текст и применяет шрифты согласно стилям элементов.
  • Границы (Borders): Отрисовка границ элементов с учетом их стилей.
  • Изображения (Images): Размещение изображений в соответствии с их стилями.
  • Прозрачность (Transparency): Обработка прозрачности элементов и их взаимодействие с фоном и другими элементами.

Composite

Composition использует идею слоев для оптимизации. Когда изменяется какой-либо элемент на странице, вместо того чтобы перерисовывать всю страницу, браузер может перерисовать только тот слой, который действительно изменился. Это уменьшает объем работ, который требуется выполнить для обновления экрана.
Преимущества использования Composition включают уменьшение времени, необходимого для обновления страницы, и снижение нагрузки на процессор и графическую подсистему. Это особенно важно при анимациях, перетаскивании элементов и других сценариях, требующих частого обновления экрана.

1.compositing-element {
2  width: 100px;
3  height: 100px;
4  background-color: blue;
5  transition: transform 0.5s ease-in-out;
6}
7
8/* Применяем will-change для оптимизации compositing */
9.compositing-element.will-change {
10  will-change: transform;
11}
1const compositingElement = document.querySelector('.compositing-element');
2  
3// Добавляем класс, который указывает на будущие изменения
4compositingElement.classList.add('will-change');
5
6// Некоторые изменения, которые могут запустить compositing
7setTimeout(() => {
8  compositingElement.style.transform = 'translateX(100px)';
9}, 1000);

В этом примере:
У нас есть элемент с классом .compositing-element, который будет изменять свое положение с помощью transform
Мы применяем CSS-свойство will-change для этого элемента, чтобы предварительно оповестить браузер о том, что произойдут изменения в transform.
После задержки в 1 секунду мы меняем transform, смещая элемент вправо.
Это может помочь браузеру оптимизировать процесс compositing, ускоряя анимацию и уменьшая нагрузку на процессор. Однако важно использовать will-changeосторожно, поскольку неправильное его использование может привести к избыточному использованию памяти.