«Цикл событий» узела является ядром его способности справляться с большой параллелизмом и высокой пропускной способности. Это самое волшебное место. Согласно этому, node.js может быть понят как «однопоточный», а также позволяет обрабатывать произвольные операции на заднем плане. Эта статья проиллюстрирует, как работают петли событий, и вы тоже можете почувствовать ее магию.
Программирование, управляемое событиями
Чтобы понять петли событий, мы должны сначала понять программирование Drive Event. Он появился в 1960 году. Сегодня программирование, управляемое событиями, широко используется в программировании пользовательского интерфейса. Одним из основных применений JavaScript является взаимодействие с DOM, поэтому естественно использовать API на основе событий.
Просто определите: управляемое событиями программирование управляет процессом применения посредством изменений в событиях или состояниях. Обычно он реализуется посредством мониторинга событий. После того, как событие обнаруживается (то есть изменение состояния), вызывается соответствующая функция обратного вызова. Звучит знакомо? Фактически, это основной принцип работы петли Node.js Event.
Если вы знакомы с развитием JavaScript на стороне клиента, подумайте об этих методах .on*(), таких как element.onclick (), которые используются для объединения с элементами DOM для прохождения взаимодействия с пользователем. Этот рабочий режим позволяет запустить несколько событий на одном экземпляре. Node.js запускает этот режим через EventEmitter (генератор событий), например, в модулях сокета и «HTTP» на стороне сервера. Одно или несколько изменений состояния могут быть вызваны из одного экземпляра.
Другой общий паттерн выражает успех и неудачу. В настоящее время есть два общих метода реализации. Первое - передать «Исключение ошибки» в обратный вызов, который обычно передается функции обратного вызова в качестве первого параметра. Второй тип - использовать режим проектирования обещаний, и ES6 был добавлен. Примечание* Режим обещания использует метод написания функций, похожей на jQuery, чтобы избежать глубокого гнездования функции обратного вызова, например:
Кода -копия выглядит следующим образом:
$ .getjson ('/getuser'). Dode (успех) .fail (Failhandler)
Модули «FS» (файловая система) в основном используют стиль передачи исключений в обратный вызов. Технически запускает определенные вызовы, такие как прикрепленное событие fs.readfile (), но API используется только для того, чтобы напомнить пользователю, чтобы выразить успех или сбой работы. Такой API выбирается по архитектурным причинам, а не на технических ограничениях.
Распространенным заблуждением является то, что излучатели событий также по своей природе асинхронны при запуска событий, но это неверно. Вот простой фрагмент кода, чтобы доказать это.
Кода -копия выглядит следующим образом:
Функция myeMitter () {
Eventemitter.call (это);
}
util.inherits (myemitter, eventemitter);
Myemitter.prototype.dostuff = function dostuff () {
console.log ('до')
Emitter.emit ('Fire')
console.log ('после')}
};
var me = new myemitter ();
me.on ('fire', function () {
console.log ('Emit уволен');
});
me.dostuff ();
// Выход:
// до
// Имитет
// после
Примечание* Если Emitter.emit является асинхронным, выход должен быть
// до
// после
// Имитет
EventEmitter часто проявляется асинхронно, потому что он часто используется для уведомления о операциях, которые необходимо выполнять асинхронно, но сам API EventEmitter полностью синхронно. Функция прослушивания может быть выполнена асинхронно, но пожалуйста, обратите внимание, что все функции прослушивания будут выполняться синхронно в добавлении в порядке.
Обзор механизма и объединение потоков
Сам узел опирается на несколько библиотек. Одним из них является Libuv, библиотека, которая волшебным образом обрабатывает асинхронные очереди и казни.
Узел использует как можно больше существующих функций, чтобы использовать ядро операционной системы. Например, генерируется запрос ответа, подключения пересылаются и делегируются в систему для обработки. Например, входящие соединения в очереди через операционную систему до тех пор, пока они не будут обработаны узлом.
Возможно, вы слышали, что у узла есть пул потоков, и вы можете задаться вопросом: «Если узел обрабатывает задачи по порядку, зачем вам нужен бассейн потоков?» Это потому, что в ядре не все задачи выполняются асинхронно. В этом случае node.js должен быть в состоянии заблокировать поток в течение определенного периода времени, пока он работает так, чтобы он мог продолжать выполнять цикл событий без блокировки.
Ниже приведен простой пример диаграммы для показа его внутреннего рабочего механизма:
┌ackindyacside
─■ Timessers │
│ └ackindyacside
│ ┌ackindyacside
│ │ Ожидаемые обратные вызовы │
│ └ ½мобильный
│ ┌ackide
│ │ Опрос │◄ackes Connections, │
│ └ackinding
│ ┌ackideabults
─бы Setimmediate │
└ackindyacside
Есть некоторые трудности в понимании внутреннего механизма операции цикла событий:
Все обратные вызовы предварительно установлены через процесс. Это позволит избежать потенциального рекурсивного вызова для процесса.nexttick (), вызывая бесконечную петлю.
«Ожидающие обратные вызовы» - это обратный вызов в очереди обратного вызова, который не будет обрабатываться каким -либо другим циклом цикла событий (например, передано в FS.Write).
Эмиттер событий и цикл событий
Создавая EventEmitter, взаимодействие с циклами событий может быть упрощено. Это универсальная инкапсуляция, которая облегчает вам создание API на основе событий. То, как эти два взаимодействия часто заставляют разработчиков чувствовать себя смущенными.
Следующий пример показывает, что забывание о том, что событие запускается синхронно, может привести к пропущению события.
Кода -копия выглядит следующим образом:
// после v0.10, требуется ('события'). Eventemitter больше не требуется
var EventEmitter = require ('Events');
var util = require ('util');
function mything () {
Eventemitter.call (это);
dofirsthing ();
this.emit ('thing1');
}
util.inherits (mything, eventemitter);
var mt = new Mything ();
Mt.on ('thing1', function Onting1 () {
// извините, этот инцидент никогда не произойдет
});
Событие «вещь1», выше, никогда не будет поймано Mything (), потому что Mything () должен быть создан, прежде чем оно сможет выслушать события. Вот простой обходной путь, не добавляя никаких дополнительных закрытий:
Кода -копия выглядит следующим образом:
var EventEmitter = require ('Events');
var util = require ('util');
function mything () {
Eventemitter.call (это);
dofirsthing ();
setimmediate (Emitting1, это);
}
util.inherits (mything, eventemitter);
функция Emitting1 (self) {
self.emit ('thing1');
}
var mt = new Mything ();
Mt.on ('thing1', function Onting1 () {
// Выполнять
});
Следующая схема также может работать, но некоторые результаты теряются:
Кода -копия выглядит следующим образом:
function mything () {
Eventemitter.call (это);
dofirsthing ();
// Использование функции#bind () потеряет производительность
setimmediate (this.emit.bind (this, 'thing1'));
}
util.inherits (mything, eventemitter);
Другая проблема - вызвать ошибку (исключение). Уже сложно узнать проблему в вашем приложении, но без стека вызовов (примечание *e.stack) отладка практически невозможно. Стек вызовов будет потерян, когда ошибка будет запрошена удаленным асинхронно. Существует два возможных решения: синхронное запуск или обеспечение того, чтобы ошибка была передана с другой важной информацией. Следующий пример демонстрирует эти два решения:
Кода -копия выглядит следующим образом:
Mything.prototype.foo = function foo () {
// эта ошибка будет вызвана асинхронно
var er = dofirSthing ();
if (er) {
// При запуска вам необходимо создать новую ошибку, которая сохраняет информацию о стеке вызовов на месте.
setimmediate (Emiterror, это, новая ошибка («плохие вещи»));
возвращаться;
}
// запустить ошибку и немедленно обработать ее (синхронизировать)
var er = dosecondthing ();
if (er) {
this.emit ('error', 'больше плохих вещей ");
возвращаться;
}
}
Оценить ситуацию. Когда ошибка запускается, ее можно обработать немедленно. В качестве альтернативы, это может быть тривиальным и может быть легко обработано или обработано позже. Кроме того, передача ошибки через конструктор не является хорошей идеей, потому что экземпляр построенного объекта, вероятно, будет неполным. Исключением является случай, когда ошибка была прямо сейчас брошена.
Заключение
В этой статье кратко рассматривается внутренний операционный механизм и технические детали цикла событий. Все тщательно продуманы. Другая статья будет обсуждаться взаимодействие между циклами событий и системными ядрами и продемонстрирует магию узлов, работающих асинхронно.