JavaScript event loop в картинках (часть 1)

Pavel Bely
Jun 27, 2017 · 2 min read

Недавно наткнулся на проблему “зависания” UI web-приложения по время выполнения тяжелого скрипта, который, как выяснилось, “узурпировал” event loop.

В приложениии, которое мы разрабатываем, пользователь может добавлять товары — вводить информацию о товаре и его изображения. Изображения напрямую с телефона пользователя отсылаются в Amazon S3, но прежде прямо на клиенте предуменьшаются, чтобы ускорить процесс их загрузки. И на этом этапе обработки картинок UI зависает на пару секунд (это особенно ощутимо на мобильном телефоне). Чтобы решить эту проблему мне пришлось разобраться, как он работает, чем и хочу с вами поделиться.

Итак, event loop, или иначе — событийный цикл.

Движок браузера выполняет JavaScript в одном потоке. Он не может поставить обработку события на паузу, переключиться на другое событие, а после — возобновить выполнение первого. Все события обрабатываются последовательно и каждое — до победного конца.

Для вышеописанного потока выделяется область памяти — стэк, где хранятся фреймы (аргументы, локальные переменные) вызываемых функций.

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


Browser events

Какие же события происходят в браузере? Их :
- клики мышкой;
- скроллинг;
- ввод с клавиатуры;
- загрузка скриптов;
- CSS анимации;
- и тд.

Браузер может реагировать на эти события. Для этого событию нужно , то есть функцию, которая сработает, когда событие произошло. Функция выполнится не сразу, она станет в конец очереди событий и выполнится, когда придёт её время.


setTimeout()

Можно настроить добавление функции в очередь событий по таймеру, т.е. по прошествии некоторого времени. Это позволяет сделать функция :

var timeoutID = scope.setTimeout(function[, delay, param1, param2, …]);

Первым аргументом передаётся функция, выполнение который мы хотим отложить. Вторым аргументом можно указать время, через которое функция будет добавлена в очередь событий. Если его не указывать, то фукнция будет добавлена в конец очереди немедленно.

Например, таким образом фукнция greet будет добавлена в очередь через 3 с:

setTimeout(function greet() {
console.log("hey ho");
}, 3000);

Итого

Обработчики назначены, события происходят и попадают в очередь. И обрабатываются в event loop. Вот так схематично (и крайне упрощенно) можно его представить:

while (queue.waitForMessage()) {
queue.processNextMessage();
}

Это по сути бесконечный цикл, в котором выполняются многочисленные обработчики событий. Если очередь пустая — движок браузера ждет, когда поступит событие. Если непустая — первое в ней событие извлекается и его обработчик начинает выполняться. И так до бесконечности.


Вы спросите, а где, собственно, обещанные в заглавии картинки?
Так вот, они — , где мы рассмотрим случай с реального проекта, когда пришлось вникнуть поглубже в event loop.

Credits

Pavel Bely

Written by

Software engineer, punk musician