Please note, this is a STATIC archive of website developer.mozilla.org from November 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Параллельная модель и цикл событий.

Параллелизм в JavaScript основывается на модели "событийного цикла". Эта модель отличается от модели других языков, например C или Java.

Концепция жизненного цикла программы

В следующей секции, объясняется теоретическая модель. Современные JavaScript движки существенно оптимизируют этот процесс.

Визуальное представление

Stack, heap, queue

Стек

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

function f(b) {
  var a = 12;
  return a + b + 35;
}

function g(x) {
  var m = 4;
  return f(m * x);
}

g(21);

Когда вызывается g,  создаётся область видимости, содержащая аргументы g и локальные переменные. Когда g вызывает f, создаётся вторая область видимости и помещается в стэк вперед первой, которая содержит аргументы f и её локальные переменные. Когда f возвращает результат, верхний элемент из стека удаляется. Когда g возвращает результат, ее контекст также удалится, и стэк будет пуст.

Куча

Объекты размещаются в куче. Куча — это ссылка на определённую неструктурированную области памяти.

Очередь событий

Жизненный цикл JavaScript имеет очередь событий. Очередь событий — это список событий, подлежащих обработке. Каждое событие связано с функцией. Когда стэк пуст, событие извлекается из очереди и обрабатывается. Вызывается соответствующая функция (и, таким образом, создаётся начальный элемент стека). Обработка событий заканчивается, когда стек снова становится пустым.

Цикл событий

Модель событийного цикла (event loop) называется так, потому что отслеживает новые события в цикле:

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

queue.waitForMessage ожидает события если очередь пуста.

Запуск до завершения

Каждое событие должно быть полностью обработано, прежде чем начнет обрабатываться следующее. Благодаря этому мы можем точно знать: когда бы и где бы не выполнялась функция – она не может быть упреждающей, и полностью выполнится до того, как начнет выполняться другой код (который может изменять данные, с которыми работает текущая функция). Это отличается от, например, языка программирования C, где функция запускается в потоке. В этом случае она может быть остановлена в любой момент для запуска другого кода в другом потоке.

У данного подхода есть и минусы. Если событие обрабатывается слишком долго, то приложение в это время не имеет возможности обработать действия пользователя (например, скролл, или клик). Браузер может смягчать последствия такой проблемы. Internet Explorer, в таком случае, выводит сообщение "A script on this page is causing Internet Explorer to run slowly" и предлагает завершить обработку слишком тяжелого события. Хорошая практика для предохранения от этого – не создавать события, которые могут выполняться долго, и разбивать большие события на несколько мелких.

Добавление событий в очередь

В браузерах события добавляются в очередь в любое время, если событие произошло, а так же если у него есть обработчик. В случае, если обработчика нет – событие потеряно. Так, клик по элементу, имеющему обработчик события по событию click – добавит событие в очередь, а если обработчика нет – то и событие в очередь не попадет.

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

Связь нескольких потоков между собой

Web Worker или кросс-доменный фрейм имеют свой собственный стэк, кучу и очередь событий. Два отдельный событийных потока могут связываться друг с другом, только через отправку сообщений с помощью метода postMessage. Этот метод добавляет сообщение  в очередь другого, если он конечно принимает их.

Никогда не блокируется

Очень интересное свойство цикла событий в JavaScript, что в отличие от множества других языков, поток выполнения никогда не блокируется. Обработка I/O обычно осуществляется с помощью событий и функций обратного вызова, поэтому даже когда приложение ожидает запрос от IndexedDB или ответ от XHR, оно может обрабатывать другие процессы, например пользовательский ввод.

Существуют хорошо известные исключения как alert или синхронный XHR, но считается хорошей практикой избегать их использования.

Метки документа и участники

Метки: 
 Внесли вклад в эту страницу: forestbird, Saviloff, pashutk, fscholz, im7mortal
 Обновлялась последний раз: forestbird,