Node.js开发指南(三)回调函数与事件

让我们看看在 Node.js 中如何用异步的方式读取一个文件,下面是一个例子:     

//readfile.js 
var fs = require('fs'); 
fs.readFile('file.txt', 'utf-8', function(err, data) { 
 if (err) { 
 console.error(err); 
 } else { 
 console.log(data); 
 } 
}); 
console.log('end.');

运行的结果如下:

end. 
Contents of the file.

Node.js 也提供了同步读取文件的 API

//readfilesync.js 
var fs = require('fs'); 
var data = fs.readFileSync('file.txt', 'utf-8'); 
console.log(data); 
console.log('end.');

运行的结果与前面不同,如下所示:

$ node readfilesync.js 
Contents of the file. 
end.

        同步式读取文件的方式比较容易理解,将文件名作为参数传入 fs.readFileSync 数,阻塞等待读取完成后,将文件的内容作为函数的返回值赋给 data 变量,接下来控制台 输出 data 的值,最后输出 end.

        异步式读取文件就稍微有些违反直觉了,end.先被输出。要想理解结果,我们必须先 知道在 Node.js 中,异步式 I/O 是通过回调函数来实现的。fs.readFile 接收了三个参数, 第一个是文件名,第二个是编码方式,第三个是一个函数,我们称这个函数为回调函数。 JavaScript 支持匿名的函数定义方式,譬如我们例子中回调函数的定义就是嵌套fs.readFile 的参数表中的。这种定义方式在 JavaScript 程序中极为普遍,与下面这种定义 方式实现的功能是一致的:

//readfilecallback.js 
function readFileCallBack(err, data) { 
 if (err) { 
 console.error(err); 
 } else { 
 console.log(data); 
 } 
} 
var fs = require('fs'); 
fs.readFile('file.txt', 'utf-8', readFileCallBack); 
console.log('end.');

        fs.readFile 调用时所做的工作只是将异步式 I/O 请求发送给了操作系统,然后立即 返回并执行后面的语句,执行完以后进入事件循环监听事件。当 fs 接收到 I/O 请求完成的 事件时,事件循环会主动调用回调函数以完成后续工作。因此我们会先看到 end.,再看到 file.txt 文件的内容。

       

Node.js 中,并不是所有的 API 都提供了同步和异步版本。Node.js 不 鼓励使用同步 I/O

         Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。在开发者看来,事 件由 EventEmitter 对象提供。前面提到的 fs.readFile http.createServer 的回 调函数都是通过 EventEmitter 来实现的。下面我们用一个简单的例子说明 EventEmitter 的用法:

//event.js 
var EventEmitter = require('events').EventEmitter; 
var event = new EventEmitter(); 
event.on('some_event', function() { 
 console.log('some_event occured.'); 
}); 
setTimeout(function() { 
 event.emit('some_event'); 
}, 1000);

        运行这段代码,1秒后控制台输出了 some_event occured.。其原理是 event 对象 注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 1000毫秒以后向 event 对象发送事件 some_event,此时会调用 some_event 的监听器。

       Node.js 的事件循环机制 Node.js 在什么时候会进入事件循环呢?答案是 Node.js 程序由事件循环开始,到事件循 环结束,所有的逻辑都是事件的回调函数,所以 Node.js 始终在事件循环中,程序入口就是 事件循环第一个事件的回调函数。事件的回调函数在执行的过程中,可能会发出 I/O 请求或 直接发射emit)事件,执行完毕后再返回事件循环,事件循环会检查事件队列中有没有未 处理的事件,直到程序结束。图3-5说明了事件循环的原理。

        与其他语言不同的是,Node.js 没有显式的事件循环,类似 Ruby EventMachine::run() 的函数在 Node.js 中是不存在的。Node.js 的事件循环对开发者不可见,由 libev 库实现。libev 支持多种类型的事件,如 ev_ioev_timerev_signalev_idle 等,在 Node.js 中均被 EventEmitter 封装。libev 事件循环的每一次迭代,在 Node.js 中就是一次 Ticklibev 断检查是否有活动的、可供检测的事件监听器,直到检测不到时才退出事件循环,进程结束。

猜你喜欢

转载自blog.csdn.net/zhuoganliwanjin/article/details/89466391