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); 
  }); 
  });

nodejs 循环的陷阱的更多相关文章

  1. shell中while循环的陷阱

    在写while循环的时候,发现了一个问题,在while循环内部对变量赋值.定义变量.数组定义等等环境,在循环外面失效. 一个简单的测试脚本如下: #!/bin/bash echo "abc ...

  2. nodejs 循环中操作需要同步执行解决方案

    最近用nodejs做了个针对某网站的小爬虫.干坏事得低调对吧,不能同时开太多的网络访问,结果各种回调/循环虐的心力交瘁. 经过了n次的百度\哥哥后终于拼出了自己要的功能.不敢独享分享出来以供大家参考. ...

  3. js for循环的陷阱

    ☞问题概述 一页面有三个按钮,点击提示相应内容.相应内容已从后台获取,并转化成json数组. var content = ["提示1", "提示2", &quo ...

  4. js中FOR循环的陷阱

    //闭包解决 循环输出的问题 for(var i=0;i<rows.length;i++) {( function (i) { })(i);

  5. Java日志——2016.6.3

    1)二维数组的静态初始化:                int[][] arr = new int[][] { {1,2,3}, {4,5}, {6,7}} 2)杨辉三角: /** *    需求: ...

  6. 完美C++(第5版)(双色)

    完美C++(第5版)(双色) 薛正华 沈庚 韦远科 译 ISBN 978-7-121-23198-8 2014年6月出版 定价:148.00元 788页 16开 内容提要 <完美C++(第5版) ...

  7. Node.js模块 加载笔记

    //核心模块就是Node.js标准API种提供的模块,如fs,http,net.vm等.官方提供,编译成二进制代码//核心模块拥有最高的加载优先级 //文件模块则是存储为单独的文件(或文件夹)的模块, ...

  8. PageRank之基于C C#的基本实现

    重点不是说PageRank是什么,而是怎么用代码实现 什么是PageRank? PageRank,网页排名,又称网页级别.Google左侧排名或佩奇排名,是一种由[1]  根据网页之间相互的超链接计算 ...

  9. Linux和Shell回炉复习系列文章总目录

    本页内容都是本人回炉Linux时整理出来的.这些文章中,绝大多数命令类内容都是翻译.整理man或info文档总结出来的,所以相对都比较完整. 本人的写作方式.风格也可能会让朋友一看就恶心到直接右上角叉 ...

随机推荐

  1. AVL平衡树的插入例程

    /* **AVL平衡树插入例程 **2014-5-30 11:44:50 */ avlTree insert(elementType X, avlTree T){ if(T == NULL){ T = ...

  2. matlab2016b -ubuntu 1604 -install- and -trouble -shooting--finally-all is ok!!

    Linux系统下安装matlab2016b 标签: ubuntumatlablinux 2016-09-24 22:11 16203人阅读 评论(22) 收藏 举报 分类: linux 版权声明:本文 ...

  3. Install Server Backup Manager on CentOS, RHE, and Fedora

    Skip to end of metadata Added by Internal, last edited by Internal on Aug 25, 2014 Go to start of me ...

  4. HSSFWorkbook

    public ActionResult excelPrint() { HSSFWorkbook workbook = new HSSFWorkbook();// 创建一个Excel文件 HSSFShe ...

  5. SQLMAP源码分析(一)

    说起来,学习Python很大一部分原因是由于对WEB安全的兴趣以及对SQLMAP这款工具的好奇,曾经设想学完Python基础就读一读SQLMAP源码,然而懒病一犯,随之就大江东去.近来,又重新燃起了读 ...

  6. NYOJ 905 卡片游戏

    卡片游戏 时间限制:1000 ms  |  内存限制:65535 KB 难度:1 描写叙述 小明近期宅在家里无聊.于是他发明了一种有趣的游戏.游戏道具是N张叠在一起的卡片,每张卡片上都有一个数字,数字 ...

  7. 多线程(C++)临界区Critical Sections

    一 .Critical Sections(功能与Mutex相同,保证某一时刻只有一个线程能够访问共享资源,但是不是内核对象,所以访问速度比Mutex快,但是没有等待超时的功能,所以有可能导致死锁,使用 ...

  8. db2 命令

    很久没有些博客了.把以前用到的操作 DB2 的命令发表下可能有很多人已经发布了.就当是自己做下功课吧,以备有用之需. 1. 打开命令行窗口 #db2cmd 2. 打开控制中心 # db2cmd db2 ...

  9. pygame 安装教程

    步骤: 1.去官网下载PyGame 注意:要下载对应版本的包 官网地址:http://www.pygame.org/download.shtml 其中,如果python为以下版本: python 3. ...

  10. Google Guava之Optional优雅的使用null

    为什么使用optional 使用Optional<T>除了简化粗鲁的if(null == object).降低函数的复杂度.增加可读性之外,它是一种傻瓜式的防护,Optional<T ...