龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > Javascript编程 >

走近Node.js的异步代码设计(1)(2)

时间:2013-03-06 14:58来源:未知 作者:admin 点击:
分享到:
并行处理 异步代码特别适合操作I/O操作的并行处理:代码的执行并不因I/O调用的返回而受阻。多个I/O操作可以并行开始。在下面示例中,某个目录中所有

并行处理

异步代码特别适合操作I/O操作的并行处理:代码的执行并不因I/O调用的返回而受阻。多个I/O操作可以并行开始。在下面示例中,某个目录中所有文件的大小都在循环中累加,以获得那些文件占用的总字节数。使用异步代码,循环的每次迭代都必须等到获取单个文件大小的I/O调用返回为止。

异步代码允许快速连续地在循环中开始所有I/O调用,不用等结果返回。只要其中一个I/O操作完成,回调函数就被调用,而该文件的大小就可以添加到总字节数中。

唯一必不可少的有一个恰当的停止标准,它决定着我们完成处理后,就计算所有文件的总字节数。

同步

  1. var fs = require('fs');  
  2. function calculateByteSize() {  
  3.     var totalBytes = 0,  
  4.         i,  
  5.         filenames,  
  6.         stats;  
  7.     filenames = fs.readdirSync(".");  
  8.     for (i = 0; i < filenames.length; i ++) {  
  9.         stats = fs.statSync("./" + filenames[i]);  
  10.         totalBytes += stats.size;  
  11.     }  
  12.     console.log(totalBytes);  
  13. }  
  14.  
  15.  
  16.  
  17. calculateByteSize(); 

异步

  1. var fs = require('fs');  
  2. var count = 0,  
  3.     totalBytes = 0;  
  4. function calculateByteSize() {  
  5.     fs.readdir(".", function (err, filenames) {  
  6.         var i;  
  7.         count = filenames.length;  
  8.         for (i = 0; i < filenames.length; i++) {  
  9.             fs.stat("./" + filenames[i], function (err, stats) {  
  10.                 totalBytes += stats.size;  
  11.                 count--;  
  12.                 if (count === 0) {  
  13.                     console.log(totalBytes);  
  14.                 }  
  15.             });  
  16.         }  
  17.     });  
  18. }  
  19. calculateByteSize(); 

同步示例简单又直观。在异步版本中,第一个fs.readdir()被调用,以读取目录中的文件名称。在回调函数中,针对每个文件调用fs.stat(),返回该文件的统计信息。这部分不出所料。

值得关注的方面出现在计算总字节数的fs.stat()回调函数中。所用的停止标准是目录的文件数量。变量count以文件数量来初始化,倒计数回调函数执行的次数。一旦数量为0,所有I/O操作都被回调,所有文件的总字节数被计算出来。计算完毕后,字节数可以记录到控制台。

异步示例有另一个值得关注的特性:它使用闭包(closure)。闭包是函数里面的函数,内层函数访问外层函数中声明的变量,即便在外层函数已完成之后。fs.stat()回调函数是闭包,因为它早在fs.readdir()回调函数完成后,访问在该函数中声明的count和totalBytes这两个变量。闭包有关于它自己的上下文。在该上下文中,可以放置在函数中访问的变量。

要是没有闭包,count和totalBytes这两个变量都必须是全局变量。这是由于fs.stat()回调函数没有放置变量的任何上下文。calculateBiteSize()函数早已结束,只有全局上下文仍在那里。这时候闭包就能派得上用场。变量可以放在该上下文中,那样可以从函数里面访问它们。

代码复用

代码片段可以在JavaScript中复用,只要把代码片段包在函数里面。然后,可以从程序中的不同位置调用这些函数。如果函数中使用了I/O操作,那么改成异步代码时,就需要某种重构。

下面的异步示例显示了返回某个目录中文件数量的函数countFiles()。countFiles()使用I/O操作fs.readdirSync() 来确定文件数量。span style="font-family: courier new,courier;">countFiles()本身被调用,使用两个不同的输入参数:

同步

  1. var fs = require('fs');  
  2. var path1 = "./",  
  3.     path2 = ".././";  
  4. function countFiles(path) {  
  5.     var filenames = fs.readdirSync(path);  
  6.     return filenames.length;  
  7. }  
  8. console.log(countFiles(path1) + " files in " + path1);  
  9. console.log(countFiles(path2) + " files in " + path2); 

异步

  1. var fs = require('fs');  
  2. var path1 = "./",  
  3.     path2 = ".././",  
  4.     logCount;  
  5. function countFiles(path, callback) {  
  6.     fs.readdir(path, function (err, filenames) {  
  7.         callback(err, path, filenames.length);  
  8.     });  
  9. }  
  10. logCount = function (err, path, count) {  
  11.     console.log(count + " files in " + path);  
  12. };  
  13. countFiles(path1, logCount);   
  14. countFiles(path2, logCount);  

把fs.readdirSync()换成异步fs.readdir()迫使闭包函数cntFiles()也变成异步,因为调用cntFiles()的代码依赖该函数的结果。毕竟,只有fs.readdir()返回后,结果才会出现。这导致了cntFiles()重构,以便还能接受回调函数。整个控制流程突然倒过来了:不是console.log()调用cntFiles(),cntFiles()再调用fs.readdirSync(),在异步示例中,而是cntFiles()调用fs.readdir(),然后cntFiles()再调用console.log()。

结束语

本文着重介绍了异步编程的一些基本模式。将思路转变到异步编程绝非易事,需要一段时间来适应。虽然难度增加了,但是获得的回报是显著提高了并发性。结合JavaScript的快速周转和易于使用等优点,Node.js中的异步编程有望在企业应用市场取得进展,尤其是在新一代高度并发性的Web 2.0应用程序方面。

原文:http://shinetech.com/thoughts/thought-articles/139-asynchronous-code-design-with-nodejs
 

精彩图集

赞助商链接