цикл событий в узле js

Cikl Sobytij V Uzle Js



Node.js — это мощная платформа Javascript, которая позволяет пользователям запускать код Javascript на сервере вне браузера. Это неблокирующая, управляемая событиями среда выполнения для создания надежных масштабируемых веб-приложений. Цикл событий — важная часть Node.js, которая позволяет выполнять задачи, не дожидаясь завершения одной перед запуском другой.

Хотя Javascript является однопоточным языком, Node.js может назначать задачи операционной системе, позволяя ей обрабатывать несколько задач одновременно. Несколько задач необходимо выполнять одновременно, поскольку операции в операционной системе являются многопоточными. Обратный вызов, связанный с каждой операцией, добавляется в очередь событий и запланирован Node.js для запуска после завершения указанной задачи.

Чтобы писать эффективный и надежный код Node.js, пользователь должен иметь четкое представление о циклах событий. Это также может помочь эффективно устранять проблемы с производительностью. Цикл событий в Node.js экономит память и позволяет выполнять несколько действий одновременно, не дожидаясь завершения каждого из них. Термин «асинхронный» относится к любой функции Javascript, которая работает в фоновом режиме без блокировки входящих запросов.







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



Javascript как асинхронный язык программирования

Давайте посмотрим на концепции асинхронного программирования. Javascript используется в веб-приложениях, мобильных и настольных приложениях, но следует отметить, что Javascript — это однопоточный синхронный язык компьютерного программирования.



Для понимания концепции приведен простой пример кода.





метод функции1 ( ) {

консоль. бревно ( «Функция 1» )

}

метод функции2 ( ) {

консоль. бревно ( «Функция 2» )

}

метод1 ( )

метод2 ( )

В этом коде создаются две простые функции, и сначала вызывается метод 1, поэтому сначала регистрируется метод 1, а затем переходит к следующему.

Выход



Javascript как синхронный язык программирования

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

Javascript как язык блокировки

Будучи синхронным языком, Javascript имеет функцию блокировки. Не имеет значения, сколько времени потребуется для завершения текущего процесса, но новый процесс не будет запущен, пока не будет завершен предыдущий. В приведенном выше примере кода предположим, что в методе 1 содержится много скриптов кода, независимо от того, сколько времени это займет: 10 секунд или минута. Метод 2 не будет выполнен, пока не будет выполнен весь код в методе 1.

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

Javascript — это однопоточный язык.

Для запуска программы в JavaScript используется функциональность потока. Потоки способны выполнять только одну задачу за раз. Другие языки программирования поддерживают многопоточность и могут параллельно выполнять несколько задач, а javascript содержит только один поток для выполнения любого сценария кода.

Ожидание в Javascript

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

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

Синхронное выполнение кода на Javascript

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

Ниже приведен пример, который может помочь понять:

// приложение.js

консоль. бревно ( 'Один' )

консоль. бревно ( 'Два' )

консоль. бревно ( 'Три' )

В этом коде есть три оператора console.log, каждый из которых что-то печатает. Сначала первый оператор, который будет печатать «Один» в консоли, отправляется в стек вызовов на 1 мс (по оценкам), затем он записывается в терминал. После этого второй оператор помещается в стек вызовов, и теперь время составляет 2 мс с добавлением одного из предыдущего, а затем он записывает «Два» на консоль. Наконец, последний оператор помещается в стек вызовов (на данный момент время составляет 3 мс), и в консоли регистрируется «Три».

Приведенный выше код можно выполнить, вызвав следующую команду:

приложение узла. js

Выход

Функционирование подробно объяснено выше, и, приняв это во внимание, вывод мгновенно записывается в консоль:

Асинхронное выполнение кода в Javascript

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

// приложение.js
функция printOne ( перезвонить ) {
setTimeout ( функция ( ) {
консоль. бревно ( 'Один' ) ;
перезвонить ( ) ;
} , 1000 ) ;
}
функция printTwo ( перезвонить ) {
setTimeout ( функция ( ) {
консоль. бревно ( 'Два' ) ;
перезвонить ( ) ;
} , 2000 г. ) ;
}
функция printThree ( ) {
setTimeout ( функция ( ) {
консоль. бревно ( 'Три' ) ;
} , 3000 ) ;
}
консоль. бревно ( «Начало программы» ) ;
printOne ( функция ( ) {
printTwo ( функция ( ) {
распечататьТри ( ) ;
} ) ;
} ) ;
консоль. бревно ( «Конец программы» ) ;

В этом коде выше:

  • Объявлены три функции для печати «Один», «Два» и «Три», каждая функция имеет параметр обратного вызова, который позволяет последовательно выполнять код.
  • Тайм-аут устанавливается с помощью функции setTimeout, и существует оператор console.log для печати после определенной задержки.
  • Напечатаны два сообщения «Начало программы» и «Конец программы», обозначающие начало и конец программы.
  • Программа запускается с печати «Начала программы», после чего функция printOne выполняется с задержкой в ​​1 секунду, затем функция printTwo выполняется с задержкой в ​​2 секунды и, наконец, функция printThree выполняется с задержкой в ​​3 секунды. задерживать.
  • Программа не ожидает выполнения асинхронного кода внутри функций setTimeouts, которые регистрируют оператор «Конец программы» перед печатью One, Two и Three.

Выход

Запустите приведенный выше код, выполнив эту команду в терминале:

приложение узла. js

Теперь вывод в терминале будет отображаться асинхронно как:

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

Node.js: механизм цикла событий

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

Визуальное объяснение цикла событий в Node.js

Цикл событий в Node.js непрерывен и полубесконечен. Цикл событий вызывается при запуске сценария кода Node.js и отвечает за асинхронные вызовы API и вызовы процессов. Tick(), а также планирование таймеров, после чего возобновляется выполнение цикла событий.

В Node.js обратные вызовы обрабатываются пятью основными типами очередей:

  • «Очередь таймера», широко известная как минимальная куча, отвечает за обработку обратных вызовов, связанных с «setTimeout» и «setInterval».
  • Обратные вызовы для асинхронных операций, таких как модули «fs» и «http», обрабатываются «очередью ввода-вывода».
  • «Check Queue» содержит обратные вызовы для функции «setImmediate», уникальной для Node.
  • «Очередь закрытия» управляет обратными вызовами, связанными с событием закрытия любой асинхронной задачи.
  • Наконец, в очереди «Микрозадачи» есть две разные очереди:
    • Очередь «nextTick» содержит обратные вызовы, связанные с функцией «process.nextTick».
    • Очередь «Promise» управляет обратными вызовами, связанными с собственным Promise.

Функциональность цикла событий в Node.js

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

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

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

Теперь, когда мы глубоко понимаем цикл событий, давайте посмотрим на его особенности.

Особенности цикла событий в Node.js

Основные особенности:

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

После глубокого понимания цикла событий и логики асинхронных и синхронных выполнения понимание различных фаз может укрепить концепции цикла событий.

Фазы цикла событий Node.js

Как упоминалось выше, цикл событий полубесконечный. Он имеет много фаз, но некоторые фазы используются для внутренней обработки. Эти этапы не оказывают никакого влияния на сценарий кода.

Цикл событий повторяет функциональность Queue и выполняет задачу по принципу «первым пришел — первым обслужен». Запланированные таймеры будут обрабатываться операционной системой до истечения их срока действия. Таймеры с истекшим сроком действия затем добавляются в очередь обратного вызова для таймеров.

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

Фаза таймеров

В Node.js есть API таймера, который может планировать функции, которые должны выполняться в будущем. По истечении выделенного времени обратный вызов таймера будет выполнен, как только его можно будет запланировать; однако задержка может возникнуть либо со стороны операционной системы, либо из-за выполнения других обратных вызовов.

API таймеров имеет три основные функции:

  • setTimeout
  • setImmediate
  • setInterval

Вышеупомянутые функции являются синхронными. Область действия фазы таймера в цикле событий ограничена функциями setTimeout и setInterval. В то время как функция проверки обрабатывает функцию setImmediate.

Давайте рассмотрим простой пример, чтобы закрепить теоретическую часть:

// приложение.js

функция с задержкойФункция ( ) {

консоль. бревно ( «отложенная функция выполняется по истечении таймаута» ) ;

}

консоль. бревно ( «Начало программы» ) ;

setTimeout ( задержанная функция, 2000 г. ) ;

консоль. бревно ( «Конец программы» ) ;

В этом коде:

  • Программа запускается с регистрации оператора «Запуск программы» на терминале.
  • Затем вызывается задержанная функция с таймером 2 мс, сценарий кода не останавливается и продолжает обрабатывать задержку в фоновом режиме.
  • Оператор «Конец программы» регистрируется после первого оператора.
  • После задержки в 2 мс оператор в задержанной функции записывается на терминал.

Выход

Вывод будет выглядеть как:

Видно, что код не останавливается для обработки задержанной функции; он движется вперед, и после задержки обрабатывается обратный вызов функции.

Ожидающие обратные вызовы

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

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

Ниже приведен пример для закрепления концепции:

// приложение.js
константа фс '=' требовать ( 'фс' ) ;
функция readFileAsync ( путь к файлу, обратный вызов ) {
фс. прочитать файл ( './PromiseText.txt' , 'utf8' , функция ( эээ, данные ) {
если ( ошибаться ) {
консоль. ошибка ( ` Ошибка чтение файла : $ { ошибаться сообщение } ` ) ;
} еще {
консоль. бревно ( ` Файл содержание : $ { данные } ` ) ;
}
перезвонить ( ) ;
} ) ;
}
консоль. бревно ( «Начало программы» ) ;
чтениефилеасинк ( './PromiseText.txt' , функция ( ) {
консоль. бревно ( «Обратный вызов чтения файла выполнен» ) ;
} ) ;
консоль. бревно ( «Конец программы» ) ;

В этом коде:

  • Программа запускается путем регистрации оператора «Запуск программы» в терминале.
  • readFileAsync определяется асинхронно для чтения содержимого файла PromiseText.txt. Это параметризованная функция, которая выполняет функцию обратного вызова после чтения файла.
  • Функция readFileAsync вызывается, чтобы начать процесс чтения файла.
  • В процессе чтения файла программа не останавливается; вместо этого он переходит к следующему оператору и регистрирует его в терминале «Конец программы».
  • Асинхронное событие чтения файла обрабатывается в фоновом режиме циклом событий.
  • После того, как файл был прочитан асинхронно и его содержимое было записано на терминал, программа записывает содержимое файла на терминал. После этого он регистрирует следующее сообщение «Выполнен обратный вызов чтения файла».
  • Цикл событий обрабатывает ожидающие операции обратного вызова на следующем этапе.

Выход

Результатом вышеуказанного выполнения является:

Простой, этап подготовки в Node.js

Фаза простоя используется для работы с внутренними функциями Node.js, поэтому это не стандартная фаза. Это не влияет на сценарий кода. Фаза простоя похожа на период перерыва в цикле событий, во время которого в фоновом режиме выполняются задачи с низким приоритетом. Простой пример для понимания этого этапа:

константа { праздный } '=' требовать ( 'холостой-GC' ) ;

праздный. игнорировать ( ) ;

В этом коде используется модуль «idle-gc», позволяющий игнорировать фазу простоя. Это служит для обработки ситуаций, когда цикл событий занят и фоновые задачи не выполняются. Использование Idle.ignore не считается оптимальным, поскольку может вызвать проблемы с производительностью.

Этап опроса в Node.js

Фаза опроса в Node.js выполняет следующие функции:

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

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

  • На этапе опроса цикла событий в Node.js ожидающие события ввода-вывода ставятся в очередь, а затем выполняются в последовательной процедуре по принципу «первым вошел» и «первым вышел», пока очередь не станет пустой. Во время выполнения обратных вызовов также действуют очереди nextTick и микрозадачи. Это обеспечивает плавность и позволяет более эффективно и надежно выполнять операции ввода-вывода.
  • Если очередь пуста и сценарий не был запланирован функцией setImmediate(), цикл событий завершится и перейдет к следующему этапу (проверке). С другой стороны, если планирование сценария было выполнено с помощью функции setImmediate(), цикл событий позволяет добавлять обратные вызовы в очередь, которая будет им выполняться.

Лучше всего это проиллюстрировать простым примером кода:

setTimeout ( ( ) => {

консоль. бревно ( «Асинхронная операция завершена» ) ;

} , 2000 г. ) ;

консоль. бревно ( 'Начинать' ) ;

setImmediate ( ( ) => {

консоль. бревно ( 'Обратный вызов setImmediate выполнен' ) ;

} ) ;

консоль. бревно ( 'Конец' ) ;

В этом коде:

  • Два сообщения «Старт» и «Конец» обозначают начало и завершение работы программы.
  • Функция setTimeout() устанавливает функцию обратного вызова с задержкой 2 мс и регистрирует на терминале «Асинхронная операция завершена».
  • Функция setImmediate() регистрирует сообщение «выполнен обратный вызов setImmediate» на терминал после того, как на терминал было записано стартовое сообщение.

Выход

В выводе будут показаны сообщения с минутным наблюдением о том, что «асинхронная операция завершена» требует времени и печатается после сообщения «Конец»:

Фаза проверки Node.js

После выполнения фазы опроса выполняются обратные вызовы на этапе проверки. Если сценарий кода запланирован с использованием функции setImmediate() и функция опроса свободна, цикл событий работает, переходя непосредственно к фазе проверки, а не бездействуя. Функция setImmediate() — это уникальный таймер, который работает на разных этапах цикла событий.

API libuv используется для планирования выполнения обратного вызова после завершения фазы опроса. Во время выполнения кода цикл событий переходит в фазу опроса, в которой он ожидает входящие запросы на соединение. В другом случае, если обратный вызов запланирован с использованием функции setImmediate() и фаза опроса завершается без каких-либо действий, вместо ожидания она перейдет к фазе проверки. Для понимания рассмотрим приведенный ниже пример:

// приложение.js

консоль. бревно ( 'Начинать' ) ;

setImmediate ( ( ) => {

консоль. бревно ( «Немедленный обратный звонок» ) ;

} ) ;

консоль. бревно ( 'Конец' ) ;

В этом коде на терминал записываются три сообщения. Затем функция setImmediate() наконец отправляет обратный вызов для регистрации сообщения « Немедленный обратный звонок » к терминалу.

Выход

Вывод приведенного выше кода будет отображаться в следующей последовательности:

Обратные вызовы закрытия Node.js

Node.js использует эту фазу закрытия для запуска обратных вызовов для закрытия событий и завершения итерации цикла событий. После закрытия соединения цикл событий обрабатывает события закрытия на этом этапе. На этом этапе цикла событий «nextTick()» и микрозадачи генерируются и обрабатываются аналогично другим этапам.

Функцияprocess.exit используется для завершения цикла обработки событий в любой момент. Цикл событий проигнорирует любые ожидающие асинхронные операции, и процесс Node.js завершится.

Простой пример для рассмотрения:

// приложение.js
константа сеть '=' требовать ( 'сеть' ) ;
константа сервер '=' сеть. создатьсервер ( ( разъем ) => {
разъем. на ( 'закрывать' , ( ) => {
консоль. бревно ( «Розетка закрыта» ) ;
} ) ;
разъем. на ( 'данные' , ( данные ) => {
консоль. бревно ( «Полученные данные:» , данные. нанизывать ( ) ) ;
} ) ;
} ) ;
сервер. на ( 'закрывать' , ( ) => {
консоль. бревно ( «Сервер закрыт» ) ;
} ) ;
константа порт '=' 3000 ;
сервер. слушать ( порт, ( ) => {
консоль. бревно ( `Сервер прослушивает порт $ { порт } ` ) ;
} ) ;
setTimeout ( ( ) => {
консоль. бревно ( «Закрытие сервера через 10 секунд» ) ;
сервер. закрывать ( ) ;
процесс. Выход ( ) ;
} , 10000 ) ;

В этом коде:

  • « const net = require('net') » импортирует сетевой модуль, необходимый для работы с TCP-сервером, и « const server = net.createServer((socket) => { » создает новый экземпляр TCP-сервера.
  • « socket.on('закрыть', () => {… } ” прослушивает команду закрытия на всех сокетах. Когда соединение сокета закрывается, в терминал записывается сообщение «Socket Closed».
  • « socket.on('данные', (данные) => {} » проверяет входящие данные из всех отдельных сокетов и печатает их с помощью функции «.toString()».
  • « server.on('закрыть', () => {…} » проверяет событие «закрытия» на самом сервере, и когда соединение с сервером закрывается, оно регистрирует сообщение «Сервер закрыт» на терминал.
  • « server.listen(port, () => {…} ” прослушивает входящие соединения на порту.
  • « setTimeout(() => {…} » устанавливает таймер на 10 мс для закрытия сервера.

На этом мы завершаем обсуждение различных этапов цикла событий в Node.js. Прежде чем сделать вывод, давайте обсудим еще один момент: как выйти из цикла событий в Node.js.

Выход из цикла событий в Node.js

Цикл событий находится в фазе выполнения до тех пор, пока во всех очередях фаз цикла событий есть какие-то задачи. Цикл событий завершается после запуска фазы выхода, и обратный вызов прослушивателя выхода возвращается, если в очередях больше нет задач.

Явный способ завершить цикл обработки событий — использовать метод «.exit». Активные процессы Node.js завершатся мгновенно, как только будет вызвана функцияprocess.exit. Все запланированные и ожидающие события будут удалены:

процесс. на ( 'Выход' , ( код ) => {

консоль. бревно ( `Выход с кодом выхода : $ { код } ` ) ;

} ) ;

процесс. Выход ( 1 ) ;

Пользователи могут прослушивать функцию .exit. Следует отметить, что функция «.exit» должна быть синхронной, поскольку программа Node.js завершит работу, как только прослушает это событие.

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

Заключение

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