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. Linq实现SQL in

    比如 Id in (1,2,3) int[] a={1,2,3}; list.Where(x=>a.Contains(x.Id))

  2. 【Sprint3冲刺之前】TDzhushou软件项目测试计划书

    TDzhushou软件测试计划文档 文档编号:2014-5-8 产品版本:1.1 产品名称:TDzhushou 文 档 作 者: 解凤娇       日期:2014-5-4 软件测试计划 目录 第一章 ...

  3. laravel 配置了自己的域名以后, localhost 无法访问 404 not found 的解决方法

    这是后盾网视频教程的方法,应该是配置虚拟主机,此方法要改动,apache服务器里的conf文件夹里的httpd.conf文件 和conf/extral里面的httpd-vhost文件 具体改动为,co ...

  4. Apcahe Shiro学习笔记(一):简介及运行官方Demo

    一.Apache Shrio: apache shiro 是一个功能强大和易于使用的Java安全框架,为开发人员提供一个直观而全面的的解决方案的认证,授权,加密,会话管理. 支持认证跨一个或多个数据源 ...

  5. Linux快捷键和vim快捷键

    系统下常用快捷键   ctrl+左右键      在单词之间跳转 Ctrl + a            光标移动到行首(ahead of line),相当于通常的Home键 Ctrl + e     ...

  6. 怎么理解RSA算法

    原文地址:http://www.ittenyear.com/414/rsa/ 怎么理解RSA算法 能够把非对称加密算法里的公钥想象成一个带锁的箱子,把私钥想象成一把钥匙 能够把对称加密算法里的密钥想象 ...

  7. 51 NOD 1753 相似子串 字符串hash

      1735  相似子串  基准时间限制:5 秒 空间限制:131072 KB 分值: 80   两个字符串相似定义为:1.两个字符串长度相等2.两个字符串对应位置上有且仅有至多一个位置所对应的字符不 ...

  8. 设置port转发来訪问Virtualbox里linux中的站点

    上一篇中我们讲到怎么设置virtuabox来通过SSH登录机器. 相同.我们也能够依照上一篇内容中的介绍,设置port转发,来訪问虚拟linux系统已经搭建的站点: 1.设置port转发: water ...

  9. oracle rac常用的网络检查命令

    oracle的集群管理软件和数据库对私网依赖性很大,很多集群问题最后都可以归结到网络层面. 当集群出现问题时检查网络信息是必要的. 1.查看MTU的大小,确认所有节点的公网和私网网卡的MTU大小相同 ...

  10. Windows程序设计(1)——Win32运行原理(二)

    创建进程 1 进程和线程 2 应用程序的启动过程 3 CreateProcess函数 4 实例 3 创建进程 3.1 进程和线程 进程通常被定义为一个存在运行的程序的实例.进程是一个正在运行的程序,它 ...