走近Node.js的异步代码设计(1)
【51CTO精选译文】许多企业目前在评估Node.js的异步、事件驱动型的I/O,认为这是一种高性能方案,可以替代多线程企业应用服务器的传统同步I/O。异步性质意味着,企业开发人员必须学习新的编程模式,忘掉旧的编程模式。他们必须彻底转变思路,可能需要借助电击疗法^_^。本文介绍了如何将旧的同步编程模式换成全新的异步编程模式。
51CTO推荐专题:Node.js专区
开始转变思路
要使用Node.js,就有必要了解异步编程的工作原理。异步代码设计并非简单的设计,需要一番学习。现在需要来一番电击疗法:本文在同步代码示例旁边给出了异步代码示例,表明如何更改同步代码,才能变成异步代码。这些示例都围绕Node.js的文件系统(fs)模块,因为它是唯一含有同步I/O操作及异步I/O操作的模块。有了这两种示例,你可以开始转变思路了。
相关代码和独立代码
回调函数(callback function)是Node.js中异步事件驱动型编程的基本构建模块。它们是作为变量,传递给异步I/O操作的函数。一旦操作完成,回调函数就被调用。回调函数是Node.js中实现事件的机制。
下面显示的示例表明了如何将同步I/O操作转换成异步I/O操作,并显示了回调函数的使用。示例使用异步fs.readdirSync()调用,读取当前目录的文件名称,然后把文件名称记录到控制台,最后读取当前进程的进程编号(process id)。
同步
- var fs = require('fs'),
- filenames,
- i,
- processId;
- filenames = fs.readdirSync(".");
- for (i = 0; i < filenames.length; i++) {
- console.log(filenames[i]);
- }
- console.log("Ready.");
- processprocessId = process.getuid();
异步
- var fs = require('fs'),
- processId;
- fs.readdir(".", function (err, filenames) {
- var i;
- for (i = 0; i < filenames.length; i++) {
- console.log(filenames[i]);
- }
- console.log("Ready.");
- });
- processprocessId = process.getuid();
在同步示例中,处理器等待fs.readdirSync() I/O操作,所以这是需要更改的操作。Node.js中该函数的异步版本是fs.readdir()。它与fs.readdirSync()一样,但是回调函数作为第二个参数。
使用回调函数模式的规则如下:把同步函数换成对应的异步函数,然后把原先在同步调用后执行的代码放在回调函数里面。回调函数中的代码与同步示例中的代码执行一模一样的操作。它把文件名称记录到控制台。它在异步I/O操作返回之后执行。
就像文件名称的记录依赖fs.readdirSync() I/O操作的结果,所列文件数量的记录也依赖其结果。进程编号的存储独立于I/O操作的结果。因而,必须把它们移到异步代码中的不同位置。
规则就是将相关代码移到回调函数中,而独立代码的位置不用管。一旦I/O操作完成,相关代码就被执行,而独立代码在I/O操作被调用之后立即执行。
顺序
同步代码中的标准模式是线性顺序:几行代码都必须下一行接上一行来执行,因为每一行代码依赖上一行代码的结果。在下面示例中,代码首先变更了文件的访问模式(比如Unix chmod命令),对文件更名,然后检查更名后文件是不是符号链接。很显然,该代码无法乱序运行,不然文件在模式变更前就被更名了,或者符号链接检查在文件被更名前就执行了。这两种情况都会导致出错。因而,顺序必须予以保留。
同步
- var fs = require('fs'),
- oldFilename,
- newFilename,
- isSymLink;
- oldFilename = "./processId.txt";
- newFilename = "./processIdOld.txt";
- fs.chmodSync(oldFilename, 777);
- fs.renameSync(oldFilename, newFilename);
- isSymLink = fs.lstatSync(newFilename).isSymbolicLink();
异步
- var fs = require('fs'),
- oldFilename,
- newFilename;
- oldFilename = "./processId.txt";
- newFilename = "./processIdOld.txt";
- fs.chmod(oldFilename, 777, function (err) {
- fs.rename(oldFilename, newFilename, function (err) {
- fs.lstat(newFilename, function (err, stats) {
- var isSymLink = stats.isSymbolicLink();
- });
- });
- });
在异步代码中,这些顺序变成了嵌套回调。该示例显示了fs.lstat()回调嵌套在fs.rename()回调里面,而fs.rename()回调嵌套在fs.chmod()回调里面。






