Browser Event Loop
Last updated
Was this helpful?
Last updated
Was this helpful?
Модель конкуррентности в JS называется Event Loop и отличается от моделей конкурретности в таких языках как С++ и Java.
JS имеет однопточный рантайм, выполняющий одну вещь за раз. Каждая вкладка в браузере выполняется в отдельном процессе своим циклом событий (event loop - EL).
Этот цикл представляет собой очередь задач, в которую добавляются такие браузерные задачи как например:
Разбор HTML
Выполнение JS кода в script
тегах
Обработка реакций на действия пользователя (движения мыши, щелчки кнопок)
Обработка результатов (асинхронных) сетевых запросов.
Схематически, JS имеет следующий рантайм:
Выполнение функций формирует стек вызова.
Куча используется для хранения объектов (большой, неструктурированный регион памяти)
Очередь событий содержит множество задач на выполнение. Каждая задача - это отдельная функция. Когда стек вызовов пустеет, следующая задача берется из очереди событий на выполнение.
Очередь событий извлекает по одной задаче и выполняет её. EL использует последовательную (run-to-completion) семантику: текущая выполняемая задача всегда завершается полностью до того как следующая задача начнет выполняться. Таким образом, каждая задача имеет полный контроль над текущим состоянием программы и программисту нет волноваться по поводу конкуррентных модификаций. По завершению текущей задачи, из очереди событий извлекается следующая и передается на исполнение в JS-Runtime.
Параллельно с EL выполняется множество других фоновых процессов (таймеры, обработка ввода), которые взаимодействуют с EL помещая в его очередь задач новые задачи на выполнение. Эти фоновые процессы запускаются как самим браузером, так и из текущего пользовательского кода. Например, выполнение запроса из JS-кода создает фоновую задачу, выполняюуюся в WebApi браузера. По завершению выполнения запроса WebApi помещает задачу обработки запроса в EL.
Все задачи, связанные с JS (script и сетевые запросы) выполняются внутри встроенного в браузер JS-движка. Js-задачи завершаются вместе с завершением выполнения соответствующего им кода. JS-движок обеспечивает выполнение JS-кода, хранит стек вызовов, управляет кучей и т.п.
A web worker or a cross-origin iframe has its own stack, heap, and message queue. Two distinct runtimes can only communicate through sending messages via the postMessage method. This method adds a message to the other runtime if the latter listens to message events.
Из последовательной семантики выполнения задач в EL следует, что не стоит писать функции блокирующие EL на длительное время --- всем остальным задачам придется ожидать.
Избежать блокирования позволяют различные методы асинхронного программирования и библиотеки:
Worker API - позволяет вынести длительные вычисления в отдельный параллельный процесс.
Asynchronous result - выполняя задачу, программист оставляет callback или другую функцию нотификации, которая будет вызвана когда задача завершится. Например, setTimeout
это асинхронный sleep
.
Существует два основных способа уведомления об асинхронном завершении задачи:
Events - для каждой задачи создается специальный объект, на котором регистрируются обработчики событий: один для успешного выполнения, другой для неудачного.
Callbacks - это функции, которые передаются для вызова в том случае, когда выполнение асинхронной операции завершилось. Недостаком Callback-ов являются более сложные сигнатуры функций, более сложная обработка ошибок и усложненная композиция функций.
Стиль программирования с использованием callback-ов часто называют CPS (continuation-passing style), поскольку следуший шаг выполнения явно передается в качестве параметра. Пример CPS:
Читать и понимать такой стиль на деле намного сложнее, чем последовательное выполнение. Для упрощения работы с асинхронными функциями в ES6 можно использовать новый инструментарий:
Promise
Generators
Сопутствующие библиотеки
Таймеры
JS имеет таймеры вида: setTimeout(callback, ms)
. Функция setTimeout
ожидает заданное число ms
а затем добавлет задачу по вычислению callback
в очередь. Стоит отметить, что ms
регулирует значение когда задача именно добавляется в EL, а не выполняется в нем.
requestAnimationFrame Функция requestAnimationFrame
позволяет координировать перерисовки в браузере.