Цикл событий Node.js

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

Node.js — это однопоточная среда выполнения JavaScript, которая использует цикл событий для обработки асинхронных операций, таких как чтение файлов, сетевые запросы и запросы к базе данных.

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

Цикл событий позволяет Node.js обрабатывать большое количество одновременных операций ввода-вывода, не вызывая блокировки потоков; это ключ к эффективной обработке параллельных запросов в Node.js.

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

Цикл событий разделён на несколько фаз, каждая из которых обрабатывает определённые задачи. Ключевые фазы перечислены ниже:

  • Таймеры: ВыполнитьsetTimeout()иsetInterval()обратные вызовы.
  • Обратные вызовы I/O: Обработка некоторых отложенных обратных вызовов ввода-вывода.
  • Idle, prepare: Используется внутренне, встречается редко.
  • Опрос: Получает новые I/O-события и выполняет связанные с I/O обратные вызовы.
  • Проверка: ВыполнитьsetImmediate()обратные вызовы.
  • Обратные вызовы закрытия: Обрабатывает обратные вызовы закрытия, напримерsocket.on('close', ...)。

Поток цикла событий

  • Задачи попадают в очередь цикла событий。
  • Цикл событий обрабатывает в порядке фаз, и каждая фаза имеет свою собственную очередь обратных вызовов.
  • Цикл событий будет вpollфазе ожидания поступления новых событий; если событий нет, он проверит колбэки в других фазах.
  • ЕслиsetImmediate()иsetTimeout()существуют оба,setImmediate()Вcheckфаза выполняется первой, в то время какsetTimeout()Вtimersфаза выполняется.

Пример

setTimeout(() => {
  console.log('Timeout callback');
}, 0);

setImmediate(() => {
  console.log('Immediate callback');
});

console.log('Main thread execution');

Порядок вывода:

  • Main thread executionВыводится первым.
  • setImmediate()иsetTimeout()Порядок выполнения зависит от текущего состояния цикла событий; обычноsetImmediate()будет выполнен первым.

Макрозадачи и микрозадачи

  • Макрозадачи:setTimeout、setInterval、setImmediate, операции ввода-вывода и т. д.
  • Микрозадачи:process.nextTick、Promise.then。

Порядок выполнения:Микрозадачи имеют более высокий приоритет, чем макрозадачи, и будут выполнены сразу после завершения колбэков текущей фазы.

Пример

setTimeout(() => {
  console.log('Timeout callback');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise callback');
});

console.log('Main thread execution');

Результат вывода выполнения:

Main thread execution
Promise callback
Timeout callback

process.nextTick()

process.nextTick() выполняет микрозадачи после завершения текущей операции и до начала следующей фазы, с более высоким приоритетом, чем Promise.

Пример

process.nextTick(() => {
  console.log('Next tick callback');
});

console.log('Main thread execution');

Вывод:

Main thread execution
Next tick callback

Событийно-ориентированная программа

В Node.js событийно-ориентированное программирование в основном реализуется через класс EventEmitter.

EventEmitter — это встроенный класс, находящийся в модуле events. Расширив EventEmitter, вы можете создать собственный эмиттер событий и регистрировать и запускать события.

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

Весь событийно-ориентированный поток реализуется именно так; это очень лаконично. Это несколько похоже на паттерн наблюдатель: событие эквивалентно субъекту (Subject), а все функции-обработчики, зарегистрированные на это событие, эквивалентны наблюдателям (Observer).

Основные понятия:

  • Событие: действие или изменение состояния, происходящее в программе, например завершение чтения файла или поступление HTTP-запроса.
  • EventEmitter:EventEmitterявляется встроенным модулем Node.js, используемым для генерации и прослушивания событий.
  • Обработчик события: функция обратного вызова, связанная с событием, вызываемая при возникновении события.

Событийно-ориентированный поток:

  • Регистрация события: В программе, черезEventEmitterэкземпляр регистрирует события и соответствующие обработчики.
  • Событие-триггер: Когда происходит указанное событие,EventEmitterоно вызовет событие.
  • Обработка события: Цикл событий планирует выполнение соответствующей функции обратного вызова для выполнения задачи.

В Node.js есть множество встроенных событий. Мы можем привязывать и прослушивать события, импортируя модуль events и создавая экземпляр класса EventEmitter, как в следующем примере:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

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

// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);

Мы можем инициировать события с помощью программы:

// 触发事件
eventEmitter.emit('eventName');

Пример

Создайте файл hello.js, код выглядит следующим образом:

Пример 1

const EventEmitter = require('events');
const myEmitter = new EventEmitter();

// Регистрация обработчика событий
myEmitter.on('greet', () => {
  console.log('Hello, world!');
});

// Вызвать событие
myEmitter.emit('greet');

Вывод:

Hello, world!

Создайте файл main.js, код выглядит следующим образом:

Пример 2

//Импортируйте модуль events var events = require('events'); //Создайте объект eventEmitter var eventEmitter = new events.EventEmitter(); //Создайте обработчик событий var connectHandler = function connected() { console.log('Соединение успешно.'); //Запустите событие data_received eventEmitter.emit('data_received'); } //Привяжите обработчик события connection eventEmitter.on('connection', connectHandler); //Используйте анонимную функцию, чтобы привязать событие data_received eventEmitter.on('data_received', function(){ console.log('Данные успешно получены.'); }); //Запустите событие connection eventEmitter.emit('connection'); console.log("Выполнение программы завершено.");

Теперь давайте выполним приведённый выше код:

$ node main.js
连接成功。
数据接收成功。
程序执行完毕。

Как работает приложение Node?

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

Теперь давайте рассмотрим предыдущий пример. Создайте файл input.txt со следующим содержимым:

Example官网地址:www.example.com

Создайте файл main.js со следующим кодом:

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
   if (err){
      console.log(err.stack);
      return;
   }
   console.log(data.toString());
});
console.log("程序执行完毕");

В приведённой выше программе fs.readFile() — это асинхронная функция, используемая для чтения файлов. Если во время чтения файла возникает ошибка, объект ошибки err выведет сообщение об ошибке.

Если ошибка не возникает, readFile пропускает вывод объекта err, а содержимое файла выводится через функцию обратного вызова.

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

程序执行完毕
Example官网地址:www.example.com

Теперь удалим файл input.txt, и результат выполнения будет следующим:

程序执行完毕
Error: ENOENT, open 'input.txt'

Поскольку файл input.txt не существует, выводится сообщение об ошибке.

Другие расширения