zoukankan      html  css  js  c++  java
  • nodejs 循环的陷阱

    Node.js 的异步机制由事件和回调函数实现,一开始接触可能会感觉违反常规,但习惯
      以后就会发现还是很简单的。然而这之中其实暗藏了不少陷阱,一个很容易遇到的问题就是
      循环中的回调函数,初学者经常容易陷入这个圈套。让我们从一个例子开始说明这个问题。 
      //forloop.js 

      var fs = require('fs'); 
      var files = ['a.txt', 'b.txt', 'c.txt']; 

      for (var i = 0 ; i < files.length; i++) { 
      fs.readFile(files[i], 'utf-8', function (err, contents) { 
      console.log(files[i] + ': ' + contents); 
      }); 
      } 
      这段代码的功能很直观,就是依次读取文件 a.txt、b.txt 、c.txt ,并输出文件名和内容。
      假设这三个文件的内容分别是 AAA 、BBB 和 CCC,那么我们期望的输出结果就是: 
      a.txt: AAA 
      b.txt: BBB 
      c.txt: CCC 
      可是我们运行这段代码的结果是怎样的呢?竟然是这样的结果: 
      undefined: AAA 
      undefined: BBB 
      undefined: CCC 
      这个结果说明文件内容正确输出了,而文件名却不对,也就意味着,contents 的结果
      是正确的,但 files[i] 的值是 undefined。这怎么可能呢,文件名不正确却能读取文件
      内容?既然难以直观地理解,我们就把 files[i] 分解并打印出来看看,在读取文件的回调
      函数中分别输出 files、i 和 files[i] 。 

      //forloopi.js 

      var fs = require('fs'); 
      var files = ['a.txt', 'b.txt', 'c.txt']; 

      for (var i = 0 ; i < files.length; i++) { 
      fs.readFile(files[i], 'utf-8', function (err, contents) { 
      console.log(files); 
      console.log(i); 
      console.log(files[i]); 
      }); 
      } 
      运行修改后的代码,结果如下: 
      [ 'a.txt', 'b.txt', 'c.txt' ] 
      3 
      undefined 
      [ 'a.txt', 'b.txt', 'c.txt' ] 
      3 
      undefined 
      [ 'a.txt', 'b.txt', 'c.txt' ] 
      3 
      undefined 
      看到这里是不是有点启发了呢?三次输出的 i 的值都是 3 ,超出了 files 数组的下标
      范围,因此 files[i] 的值就是 undefined 了。这种情况通常会在 for 循环结束时发
      生,例如 for (var i = 0; i < files.length; i++),退出循环时 i 的值就是 
      files.length 的值。既然 i 的值是 3 ,那么说明了事实上 fs.readFile 的回调函数中
      访问到的 i 值都是循环退出以后的,因此不能分辨。而 files[i] 作为 fs.readFile 的
      第一个参数在循环中就传递了,所以文件可以被定位到,而且可以显示出文件的内容。 
      现在问题就明朗了:原因是3 次读取文件的回调函数事实上是同一个实例,其中引用到
      的 i 值是上面循环执行结束后的值,因此不能分辨。如何解决这个问题呢?我们可以利用 
      JavaScript 函数式编程的特性,手动建立一个闭包: 
      //forloopclosure.js 

      var fs = require('fs'); 
      var files = ['a.txt', 'b.txt', 'c.txt']; 

      for (var i = 0 ; i < files.length; i++) { 
      ( function (i) { 
      fs.readFile(files[i], 'utf-8', function (err, contents) { 
      console.log(files[i] + ': ' + contents); 
      }); 
      })(i); 
      } 
      6.2 控制流 137 

      1 
      2 
      3 
      5 
      7 
      10 
      8 
      9 
      4 
      6 
      上面代码在 for 循环体中建立了一个匿名函数,将循环迭代变量 i 作为函数的参数传
      递并调用。由于运行时闭包的存在,该匿名函数中定义的变量(包括参数表)在它内部的函
      数( fs.readFile 的回调函数)执行完毕之前都不会释放,因此我们在其中访问到的 i 就
      分别是不同的闭包实例,这个实例是在循环体执行的过程中创建的,保留了不同的值。 
      事实上以上这种写法并不常见,因为它降低了程序的可读性,故不推荐使用。大多数情
      况下我们可以用数组的 forEach 方法解决这个问题: 
      //callbackforeach.js 

      var fs = require('fs'); 
      var files = ['a.txt', 'b.txt', 'c.txt']; 

      files.forEach(function (filename) { 
      fs.readFile(filename, 'utf-8', function (err, contents) { 
      console.log(filename + ': ' + contents); 
      }); 
      });

  • 相关阅读:
    必须转载 PHP & memcached 太精彩了,看一遍就能入门
    SYSID 来指定当前system
    How to increase the JES2 spool size
    JOBPARM SYSAFF的用处
    使用多Volume来创建一个dataset
    如何解决db2nodes.cfg line number “1”, Reason code “10”
    epdf macro to remove comments
    如何停止重复的job
    mysql数值类型
    (九)solr实现autocomplete功能
  • 原文地址:https://www.cnblogs.com/benpaodexiaopangzi/p/6253112.html
Copyright © 2011-2022 走看看