Node.js事件循环(Event Loop)和线程池详解
Node的“事件循环”(EventLoop)是它能够处理大并发、高吞吐量的核心。这是最神奇的地方,据此Node.js基本上可以理解成“单线程”,同时还允许在后台处理任意的操作。这篇文章将阐明事件循环是如何工作的,你也可以感受到它的神奇。
事件驱动编程
理解事件循环,首先要理解事件驱动编程(EventDrivenProgramming)。它出现在1960年。如今,事件驱动编程在UI编程中大量使用。JavaScript的一个主要用途是与DOM交互,所以使用基于事件的API是很自然的。
简单地定义:事件驱动编程通过事件或状态的变化来进行应用程序的流程控制。一般通过事件监听实现,一旦事件被检测到(即状态改变)则调用相应的回调函数。听起来很熟悉?其实这就是Node.js事件循环的基本工作原理。
如果你熟悉客户端JavaScript的开发,想一想那些.on*()方法,如element.onclick(),他们用来与DOM元素相结合,传递用户交互。这个工作模式允许在单个实例上触发多个事件。Node.js通过EventEmitter(事件发生器)触发这种模式,如在服务器端的Socket和“http”模块中。可以从一个单一实例触发一种或一种以上的状态改变。
另一种常见的模式是表达成功succeed和失败fail。现在一般有两种常见的实现方式。首先是将“Error异常”传入回调,一般作为第一个参数传递给回调函数。第二种即使用Promises设计模式,已经加入了ES6。注*Promise模式采用类似jQuery的函数链式书写方式,以避免深层次的回调函数嵌套,如:
$.getJSON('/getUser').done(successHandler).fail(failHandler)
“fs”(filesystem)模块大多采用往回调中传入异常的风格。在技术上触发某些调用,例如fs.readFile()附加事件,但该API只是为了提醒用户,用来表达操作成功或失败。选择这样的API是出于架构的考虑,而非技术的限制。
一个常见的误解是,事件发生器(eventemitters)在触发事件时也是天生异步的,但这是不正确的。下面是一个简单的代码片段,以证明这一点。
functionMyEmitter(){ EventEmitter.call(this); } util.inherits(MyEmitter,EventEmitter);
MyEmitter.prototype.doStuff=functiondoStuff(){ console.log('before') emitter.emit('fire') console.log('after')} };
varme=newMyEmitter(); me.on('fire',function(){ console.log('emitfired'); });
me.doStuff(); //输出: //before //emitfired //after
注*如果emitter.emit是异步的,则输出应该为 //before //after //emitfired