# Event Loop

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

### Стек <a href="#stek" id="stek"></a>

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

```javascript
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`возвращает результат, ее контекст также удалится, и стек будет пуст.

### Куча <a href="#kucha" id="kucha"></a>

Объекты размещаются в [куче](https://ru.wikipedia.org/wiki/%D0%9A%D1%83%D1%87%D0%B0_\(%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D1%8C\)). Куча — это ссылка на определенную неструктурированную область памяти.

### Очередь <a href="#ochered" id="ochered"></a>

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

## Цикл событий <a href="#cikl_sobytii" id="cikl_sobytii"></a>

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

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

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

### Запуск до завершения <a href="#zapusk_do_zaversheniya" id="zapusk_do_zaversheniya"></a>

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

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

### Добавление событий в очередь <a href="#dobavlenie_sobytii_v_ochered" id="dobavlenie_sobytii_v_ochered"></a>

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

Вызов [setTimeout](https://developer.mozilla.org/ru/docs/Web/API/WindowTimers/setTimeout) добавит событие в очередь по прошествии времени, указанного во втором аргументе вызова. Если очередь событий на тот момент будет пуста, то событие обработается сразу же, в противном случае событию функции `setTimeout` придется ожидать завершения обработки остальных событий в очереди. Именно поэтому второй аргумент `setTimeout` корректно считать не временем, через которое выполнится функция из первого аргумента, а минимальное время, через которое она сможет выполниться.

### Нулевые задержки <a href="#nulevye_zaderzhki" id="nulevye_zaderzhki"></a>

Нулевая задержка не дает гарантии, что обработчик выполнится через ноль миллисекунд. Вызов [`setTimeout`](https://developer.mozilla.org/ru/docs/Web/API/WindowTimers/setTimeout)с аргументом 0 (ноль) не завершится за указанное время. Выполнение зависит от количества ожидающих задач в очереди. Например, сообщение ''this is just a message'' из примера ниже будет выведено на консоль раньше, чем произойдет выполнение обработчика *сb1*. Это произойдет, потому что задержка – это минимальное время, которое требуется среде выполнения на обработку запроса.

```javascript
(function () {

  console.log('this is the start');

  setTimeout(function cb() {
    console.log('this is a msg from call back');
  });

  console.log('this is just a message');

  setTimeout(function cb1() {
    console.log('this is a msg from call back1');
  }, 0);

  console.log('this is the end');

})();

// "this is the start"
// "this is just a message"
// "this is the end"
// "this is a msg from call back"
// "this is a msg from call back1"
```

### Связь нескольких потоков между собой <a href="#svyaz_neskolkikh_potokov_mezhdu_soboi" id="svyaz_neskolkikh_potokov_mezhdu_soboi"></a>

Web Worker или кросс-доменный фрейм имеют свой собственный стек, кучу и очередь событий. Два отдельных событийных потока могут связываться друг с другом только через отправку сообщений с помощью метода [`postMessage`](https://developer.mozilla.org/en-US/docs/DOM/window.postMessage)`.`Этот метод добавляет`сообщение` в очередь другого, если он конечно принимает их.

## Никогда не блокируется <a href="#nikogda_ne_blokiruetsya" id="nikogda_ne_blokiruetsya"></a>

Очень интересное свойство цикла событий в JavaScript, что, в отличие от множества других языков, поток выполнения никогда не блокируется. Обработка I/O обычно осуществляется с помощью событий и функций обратного вызова, поэтому даже когда приложение ожидает запрос от [IndexedDB](https://developer.mozilla.org/ru/docs/IndexedDB) или ответ от [XHR](https://developer.mozilla.org/ru/docs/Web/API/XMLHttpRequest), оно может обрабатывать другие процессы, например: пользовательский ввод.

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