Node.js的事件循环(事件循环)和线程池的详细方案
事件驱动编程
为了理解事件周期,首先理解事件驱动的编程,它出现在1960。今天,事件驱动编程广泛应用于UI编程中。Javascript的主要用途之一是与DOM交互,所以使用基于事件的API是很自然的。
简单定义:事件驱动编程通过事件或状态的变化来控制应用程序,它通常由事件监控实现,一旦检测到事件(即状态改变),就会调用相应的回调函数。事实上,这是对Node.js的事件循环的基本原理。
如果你熟悉客户端Javascript开发,想想那些。*()方法,如element.onclick(),并结合DOM元素的迁移用户交互。这种工作方式允许将多个事件在一个单一的instance.node.js触发触发此模式通过EventEmitter(事件发生器),这样在服务器端socket和HTTP模块。一个或多个状态的变化可以从一个单一的实例触发。
另一个常见的模式是成功的成功和失败表示失败。有实现它现在常用的两种方法。第一是通过错误异常的回调,通常是作为回调函数的第一个参数传递。二是承诺的设计模式的使用,它已被添加到6。承诺模式使用jQuery的功能一样链写作方法,避免深幅回调函数的嵌套,如:
复制代码代码如下所示:
美元。getJSON( / getUser)。做(successhandler)失败(failhandler)。
大多数的FS(文件系统)模块采用来料异常的风格在后面的音。有些电话触发技术,如fs.readfile()额外的事件,但是API只是提醒用户表达的成功或失败的操作。这样一个API的选择是基于建筑考虑,而不是技术上的限制。
一个常见的误解是,事件生成器(事件发射器)在触发事件时也是自然异步的,但这是不正确的。
复制代码代码如下所示:
函数的MyEmitter(){
eventemitter.call(本);
}
util.inherits(myemitter,EventEmitter);
myemitter.prototype.dostuff =功能doStuff(){
console.log(以前)
Emitter.emit()
console.log(' ')}
};
VAR我=新MyEmitter();
Me.on(函数(){(){
console.log('emit开除);
});
Me.doStuff();
输出:
发射/发射
如果emitter.emit是异步的,输出应
发射/发射
EventEmitter往往是非常异步的,因为它往往是用来通知需要异步完成的操作,但EventEmitter API本身是完全同步的,听者内功能可以异步执行,但注意所有的听力功能将在添加顺序执行。
机制概述和线程池
节点本身依赖于多个图书馆。其中一个是libuv,对异步事件队列的魔法和图书馆的执行。
节点尽可能使用尽可能多的操作系统内核来实现现有的功能,比如生成响应请求(请求)、向前连接(连接)和委托给系统处理,例如,传入连接由操作系统管理,直到节点可以处理它们为止。
你可能听说过节点都有一个线程池,你可能想知道为什么一个线程池,如果节点将以任务过程中所需要的。这是因为在内核中,不是所有的任务都是异步执行的。在这种情况下,Node.js必须能够锁定的线程一段时间,它可以继续执行事件循环无阻。
下面是一个简单的示例图,以显示他内部的运行机制:
┌—————┐
带有/定时器/
-
屈-苯乙烯-┐
我们进行回调/悬而未决
-:-┌——┐
屈-芪┐ / /输入:
我们执行轮询连接/人。
-。
屈-芪┐--
人-人,setimmediate
---- ----
理解事件循环的内部运行机制有一定的困难:
所有的回调函数是由process.nexttick(),一个阶段的事件循环结束之前(例如,一个定时器)和下一个阶段之前,这将避免潜在的递归调用process.nexttick()和无限循环。
等待回调(等待回调)在回调队列,不会被任何其他事件循环处理回调(例如,通过FS。写)。
事件发射器和事件循环
通过创建EventEmitter,与事件循环的相互作用可以简化。这是一个普遍的封装,使它为你创建的基于事件的API更容易。这两个相互作用往往使开发商感到困惑。
下面的示例表明,被遗忘的事件是同步触发的,并可能导致事件丢失。
复制代码代码如下所示:
/ /后v0.10,不再需要('eventsEventEmitter)。
Var EventEmitter =需要('events);
VaR工具=需要('util);
功能喜欢这样事(){
eventemitter.call(本);
DoFirstThing();
This.emit('thing1);
}
util.inherits(喜欢这样事,EventEmitter);
VaR MT =新喜欢这样事();
Mt.on('thing1,功能onthing1()){
/对不起,这件事永远不会发生。
});
的above'thing1'event不会被MyThing(),因为喜欢这样事()必须被实例化听事件。下面是一个简单的解决方案,不需要添加任何额外的关闭:
复制代码代码如下所示:
Var EventEmitter =需要('events);
VaR工具=需要('util);
功能喜欢这样事(){
eventemitter.call(本);
DoFirstThing();
setimmediate(emitthing1,这个);
}
util.inherits(喜欢这样事,EventEmitter);
功能emitthing1(自我){
Self.emit('thing1);
}
VaR MT =新喜欢这样事();
Mt.on('thing1,功能onthing1()){
实现
});
下面的方案也可以工作,但会失去一些性能:
复制代码代码如下所示:
功能喜欢这样事(){
eventemitter.call(本);
DoFirstThing();
功能#绑定 / /(使用)的性能损失
setimmediate(this.emit.bind(这'thing1 '));
}
util.inherits(喜欢这样事,EventEmitter);
另一个问题是引发的错误(异常),找出应用程序中的问题是困难的,但它几乎是不可能的调试没有调用堆栈(注* e.stack)。在远程异步请求的错误调用堆栈将丢失。有两个可行的解决方案:同步触发或保证误差和其他重要的信息。以下的例子证明这两个解决方案:
复制代码代码如下所示:
mything.prototype.foo =函数foo(){
这将是错误/异步触发器。
无功二= dofirstthing();
如果(呃){
当被触发时,您需要创建一个新站点来保留调用堆栈信息错误。
setimmediate(emiterror,这新的错误(坏东西));
返回;
}
立即触发错误(同步)
无功二= dosecondthing();
如果(呃){
This.emit(错误,坏东西);
返回;
}
}
法官审时度势 u3002when误差引起的,它可以立即处理。或者,它可能是微不足道的,易于处理,或异常,将返工后。此外,通过构造函数传递错误,这不是个好主意,因为构造的对象情况有可能是不完整的。仅仅将错误直接的情况是个例外。
结语
本文讨论了事件循环的内部运行机制和技术细节,都是深思熟虑的。另一篇文章将讨论事件循环和系统内核和显示NodeJS的异步操作的魔法之间的相互作用。