Node.js 异步编程的直接体现就是回调。异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 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.');
运行的结果如下:
Node.js也提供了同步读取文件的API
//readfilesync.js
var fs = require('fs');
var data = fs.readFileSync('file.txt' , 'utf-8');
console.log(data);
console.log('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/0请求发送给了操作系统,然后立即返回并执行后面的语句,执行完以后进入事件循环监听事件。当fs接收到I/0请求完成的事件时,事件循环会主动调用回调函数以完成后续工作。因此我们会先看到end.,再看到file.txt文件的内容。