Если вы любите JavaScript, вам нужно этому научиться. Один из более глубоких аспектов JavaScript - это цикл событий. Многие разработчики могут работать годами и все еще не понимать цикл обработки событий в javascript. Однако в этом сообщении в блоге я надеюсь пролить свет на то, что такое цикл обработки событий и насколько он на самом деле не такой сложный.

Как движок Chrome V8 обрабатывает код javascript

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

Строка JavaScript, на возврат которой требуется очень много времени, может заблокировать код, написанный после нее.

Представьте, что вы находитесь на веб-сайте и нажимаете кнопку, а она просто зависает там. Вы пытаетесь нажать на другие кнопки, но нет, ничего не происходит. Наиболее вероятной причиной этого (при условии отсутствия ошибок) является нажатие кнопки, вызывающее выполнение некоторого кода JavaScript, но оно блокируется.

Стек вызовов также может быть похож на сумку для покупок: первый элемент, который попадает в корзину, является последним, и наоборот. Движок Javascript добавляет всю строку в стек вызовов и выполняет ее методом LIFO (Last in First Out). Вкратце стек вызовов - это структура данных, которая записывает и определяет, где мы находимся в программе.

Итак, как вы думаете, как движок обрабатывает эти строки кода javascript по одной за раз, это большой вопрос ???

Ответ: он использует стек вызовов.

Вы можете представить себе стек вызовов как вход в лифт: первый человек, который входит в лифт, - последний, кто выходит из лифта, тогда как последний входящий человек выходит первым.

Давайте посмотрим на пример.


var firstEventLoop = function () {  
  console.log("Yo people i am first one!!");
};

var secondEventLoop = function () {  
  firstEventLoop();
  console.log("Yo people i am second one!!");
};

secondEventLoop();

Теперь давайте посмотрим, что происходит в стеке вызовов, когда мы выполняем указанный выше файл, основная функция (анонимная функция) файла помещается в стек вызовов (показано на рисунке ниже).

Теперь в приведенном выше коде вызывается функция secondEventLoop:

После вызова secondEventLoop стек вызовов будет выглядеть, как показано ниже:

Вызов secondEventLoop вызывает вызов функции firstEventLoop

После вызова функции firstEventLoop стек вызовов будет выглядеть, как показано ниже:

Вызов функции firstEventLoop, console.log. Теперь больше нет кодов, доступных для выполнения в функции firstEventLoop, поэтому функция firstEventLoop удаляется из стека вызовов.

Выполнение продолжается в функции secondEventLoop, функции secondEventLoop console.logs. После этого больше нет доступных для выполнения кодов, поэтому функция secondEventLoop удаляется из стека вызовов.

Наконец, в этом файле больше нет строк кода для выполнения, поэтому анонимная функция файла удаляется из стека вызовов.

А теперь перейдем к следующему.

Что ж, все мы знакомы с функцией setTimeout, верно? В функции setTimeout мы можем вызвать функцию. Ну, эта функция сама по себе является асинхронной функцией обратного вызова.

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

function printMe1 () {  
  console.log("Hello Javascript");
};

function printMe2(){
   setTimeout(printMe1(),2000);
   console.log("Hey I am the first to be printed");
}
printMe2();

Выполнив приведенный выше код, сначала в консоли будет записана строка «Hey I am the first to be printed», а затем строка «Hello javascript» - надеюсь, это имеет смысл?

Теперь давайте проверим, что происходит в стеке вызовов для приведенного выше кода.

Что произойдет после setTimeout, находится в стеке вызовов, который имеет printMe в качестве функции обратного вызова. Как мы все знаем, функция printMe запускается не сразу, она запускается через 2 секунды, поэтому она не может прямо сейчас вставить функцию printMe в стек вызовов, поэтому каким-то образом она исчезает из стека вызовов.

Стек вызовов будет выглядеть следующим образом:

Выполнение продолжается с printMe2 () и печати «Это моя функция» - строка в консоли и printMe2 удаляются из стека вызовов. Теперь в стеке вызовов больше нет элементов, поэтому анонимная функция удаляется из стека вызовов.

Стек вызовов пуст, как показано ниже:

Через две секунды функция printMe2 волшебным образом появится в стеке вызовов, как показано ниже:

Он выполняет и регистрирует «Hello javascript» в консоли. Вы можете быть удивлены, как это происходит? Здесь цикл событий касается параллелизма.

Ранее мы обсуждали, что среда выполнения Javascript может делать что-то одно, но теперь мы говорим о параллелизме. Что ж, это правда, что среда выполнения javascript может делать только одну вещь за раз, мы не можем выполнять вызов Ajax во время выполнения другого кода, мы также не можем выполнять setTimeout во время выполнения другого кода, но мы можем сделать все это через Браузер. Браузер позволяет выполнять вызов Ajax во время выполнения другого кода, а также может выполнять setTimeout во время выполнения другого кода.

Выполнение setTimeout и выполнение запроса Ajax не выполняется во время выполнения Javascript, но браузер обрабатывает его как веб-API. Браузер знает о таком процессе, и мы можем получить результаты с его функцией обратного вызова.

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

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

пример блокировки ввода-вывода:

row = db_query (‘SELECT * FROM users’);

печать (строка);

print («Будет напечатано после печати результатов пользователя»);

В приведенном выше коде выполнение будет приостановлено до тех пор, пока оно не вернет результат запроса к базе данных, тогда будет выполнен оставшийся код. Итак, сначала он напечатает строку (результат запроса пользователя), а затем напечатает строку «Будет напечатано после печати результата пользователя».

Теперь рассмотрим тот же пример, но с небольшими изменениями.

пример для неблокирующего ввода-вывода:

db_query (‘SELECT * FROM users’, функция (строка) {

печать (строка);

});

print («Он будет распечатан до печати результатов пользователя»);

В приведенном выше примере используются анонимные функции, которые мы постоянно использовали в JavaScript. JS интенсивно использует события, и именно об этом говорят обратные вызовы. После завершения действия запускается событие, которое запускает обратный вызов. Вот почему ее часто называют событийной моделью или асинхронной моделью. Таким образом, в примере выполнение не будет ждать результата запроса, оно запускает запрос и продолжает другую строку кода. Как только результат будет получен из запроса, он вызывает его обратный вызов и печатает. Сначала он напечатает строку «Будет напечатан до печати результата пользователя», а затем напечатает строку (результат запроса пользователя).

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

Позвольте мне подробнее изучить цикл событий Javascript.

Давайте посмотрим, как это работает, на предыдущем примере.

Ранее мы видели, что, когда функция setTimeout помещает в стек вызовов функцию printMe1, которая является функцией обратного вызова, она исчезает из стека вызовов. Когда выполняется setTimeout, браузер помещает функцию обратного вызова setTimeout в таблицу событий. Поскольку это веб-API, он не имеет ничего общего с текущей средой выполнения javascript, поэтому он переходит в таблицу событий, чтобы зарегистрировать эту функцию и выполнить ее при наступлении определенного события (в данном случае через 2 секунды).

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

Давайте посмотрим, что произойдет после выполнения функции setTimeout.

Мы видим, что как только функция обратного вызова (printMe1 ()) перемещается в таблицу событий, в стеке вызовов ничего не блокируется. Браузер не ждет 2 секунды и продолжает выполнение из стека вызовов. Здесь следующая строка - это myFunction, она вызывается и выводит «Это моя функция» в консоли и удаляется из стека вызовов, следовательно, в стеке вызовов больше нет строк, поэтому анонимная функция также удаляется из стека вызовов, стек вызовов теперь пуст.

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

Две секунды спустя:

Функция printMe1 () перемещена из таблицы событий в очередь сообщений.

Один процесс, который постоянно проверяет, пуст ли стек вызовов. Если стек вызовов становится пустым, он проверяет, есть ли какие-либо функции, ожидающие вызова. Если да, то первая функция в таблице очереди сообщений перемещается в стек вызовов и из стека вызовов запускается.

Весь этот процесс известен как ЦИКЛ СОБЫТИЙ.

В приведенном выше примере мы видим, что функция printMe1 перемещается в стек вызовов и выполняется. Он выводит на консоль строку «Hello javascript». Сейчас все пусто.