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

Каждый раз, когда мы запускаем приложение, записывая node app.js в терминал до такой степени, что мы можем видеть какой-то вывод на консоли, между ними происходит много вещей. Создание цикла событий - одна из таких вещей.

Мы запускаем наше приложение узла, написав node app.js. Цикл событий не создается для записи, первый узел просто просматривает все содержимое наших файлов и выделяет некоторую память в куче для всех объявленных переменных. Цикл событий создается после завершения этого процесса.

Мы моделируем работу цикла событий с помощью цикла while. Выполнение тела цикла событий называется тиком . Итак, в нашем случае тело цикла while выполняется за тик.

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

Внутри этого узла функции shouldContinue js выполняет три проверки, чтобы решить, продолжит ли наш цикл обработки событий работать.

Проверка №1: есть ли какие-либо функции, которые были зарегистрированы с помощью setTimeout, setIneterval или setImmediate и все еще нуждаются в выполнении.

Проверка №2: второй узел проверки выполняет поиск любых незавершенных задач операционной системы. Примером задачи операционной системы может быть HTTP-запрос, прослушивающий некоторый порт.

Проверка № 3: третий проверочный узел выполняет поиск любых длительных операций, которые все еще выполняются в нашей программе. Примером длительной операции может быть вызов функции внутри модуля fs.

Но что на самом деле означает проверка на наличие какой-либо задачи операционной системы или какой-либо длительной операции. Что ж, я думаю, что во всех трех случаях node может создавать какой-то массив, который отслеживает все ожидающие задачи, которые могут быть созданы. Мы также будем хранить три массива внутри нашего приложения, которые будут имитировать способ отслеживания всех задач узлом, а в функции shouldContinue мы вернем длину всех трех массивов. Таким образом, пока в любом массиве есть незавершенная задача, цикл обработки событий будет продолжать выполняться еще один тик.

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

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

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

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

  1. новая незавершенная задача ОС выполнена
  2. новая ожидающая операция выполнена
  3. таймер скоро закончится

По завершении этой паузы мы переходим к последним двум шагам. На следующем шаге узел снова ищет ожидающие таймеры, но на этот раз он не имеет ничего общего с setInterval и setTimeouts. Он ищет только функции, которые были зарегистрированы с помощью setImmediate.

На последних шагах узел обрабатывает все операции закрытия. Например, закрытие файлового соединения с помощью fs.close () или другой функции, которая занимается закрытием.

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