今天和大家介绍介绍一下Node.js中核心的非阻塞IO模型及其异步调用,还有Node.js的事件驱动和EventEmitter的使用。非阻塞IO和事件驱动是Node.js的核心,在Node开发中随处可见,下面会详细介绍。
Node.js的非阻塞IO提供强大的异步调用,后面有一个事件循环进行支持,因为node多数都是非阻塞调用,所以如何处理返回的结果呢?可以使用回调函数以及EventEmitter。
一、Node.js非阻塞IO模型和异步调用
1、非阻塞IO与阻塞IO
什么是node非阻塞IO呢?这个还不能用一句话解释,这里的非阻塞IO和异步调用意思是相近的,非阻塞IO就相当于异步调用。说到非阻塞,不得不谈到阻塞调用,那么什么是阻塞调用呢?单线程程序从上而下执行就是阻塞调用,例如下面的例子:
var fs = require("fs");
console.log("start to read file.");
var data = fs.readFile(__dirname + "/package.json");
console.log(data.toString());
console.log("end.");
JS引擎由上而下执行上面的程序,先输出”start to read file.”,然后输出文件内容,后输出”end”,这种顺序执行就称为阻塞调用。这里我们要重点关注readFile这个函数,该函数用于读取文件数据,如果文件很大或者来自网络,则可能会非常耗时,但是因为是阻塞调用,所以还是要等到该函数执行完毕才能执行下一步。
另外,非阻塞IO的IO是什么意思呢?IO就是输入输出(Input/Output),也就是相对于程序数据输入输出的意思,比如程序读写文件也是一种输入输出,读写数据库也是一种输入输出,甚至和其它进程交互也是。Node的非阻塞IO处理包括了一般数据的处理方式。
2、非阻塞IO和异步调用
非阻塞IO其实就是异步调用,说到非阻塞IO不得不提一下JavaScript的单线程模型,JavaScript只有单线程,不能处理多线程的东西,要实现多线程那就要使用非阻塞IO进行异步调用,这个异步调用其实就是多线程的表现,只不过多线程中的任务是提交给其它线程进行处理的。
既然node是单线程的,那么node是如何实现非阻塞IO的呢?这就和node的事件循环有关了,如下图:
Node将异步任务放入事件队列中,这个其实就是任务队列(Task Queue),node的事件循环(Event-Loop)将任务队列中的任务取出交给其它线程处理,其它线程将处理的结果返回给主线程。
下面是读取文件使用非阻塞IO的例子:
var fs = require("fs");
console.log("start");
fs.readFile(__dirname + "/package.json", "utf-8", function(error, data){
if(!error)
console.log(data.toString());
});
console.log("end");
上面程序输出的顺序为:“start”、“end”、文件内容,此时我们使用非阻塞调用,在读取文件时,从上而下执行不需要等待文件读取完毕就可以继续向下执行,等到文件IO处理返回结果后主线程再去处理,而且要注意JavaScript的事件循环中,异步的任务返回的结果也需要等待主线程执行完毕再去处理的。
下图是比上图更为清晰的解释:
node有任何耗时的任务都可以交给其它线程异步处理,然后通过回调函数处理结果,node提供了大量非阻塞IO的函数,
二、事件驱动、回调函数和EventEmitter
Node开发基本都是基于事件驱动的,事件驱动包括注册一个事件和触发一个事件,你也可以将事件驱动理解为异步调用,那么异步调用如何处理返回结果呢?这里介绍的有两种方式:使用回调函数或者event模块中的EventEmitter。
1、使用回调函数处理异步调用的结果
例如上面例子中读取文件的非阻塞调用,如果我们需要一个确定的返回值而不是只是在该回调函数中处理,那么该如何处理呢?用一个函数封装该读取函数,传入一个回调函数,那么在调用的地方我们就可以对结果进行处理了,代码如下:
使用回调函数处理返回结果也是一种常用的方式,在ES6中你可以结合Promise一起使用。
2、使用EventEmitter处理异步调用的结果
EventEmitter是node提供的,它相当于一个通知,包括注册通知和触发通知,相对没有回调函数那么简约,注册一个通知使用on函数,触发通知使用emit函数,使用实例如下:
var fs = require("fs");
var events = require("events"); // 使用node的events模块
var emitter = new events.EventEmitter(); // 创建一个EventEmitter
fs.readFile(__dirname + "/package.json", "utf-8", function (error, data){
if(!error)
emitter.emit("onData", data); // 触发"onData"通知
});
// 接收"onData"通知
emitter.on("onData", function (data){
console.log(data);
});
评论前必须登录!
注册