2011-08-19 117 views
4

这几天我正在用NodeJS构建我的第一个项目,但是我对某个任务有点困惑,我认为这是一个简单的任务,我想问题是我对这些知识缺乏了解异步方法,但我无法在任何地方找到答案。在NodeJS中使用异步响应的异步调用

我有一个简单的循环遍历数组和任何元素,根据一些规则,我会调用一个函数或另一个。 现在某些操作会比其他操作更快,所以我最终可能会返回元素N上的函数,而不是元素N-1上的函数。 为简单起见像这样

for (var i = 0 ; i < 10 ; i++) { 
     if (i%2 === 0) { 
      setTimeout(function(i) { 
       console.log(i); 
      }, 2000); 
     } 
     else { console.log(i); } 
} 

所以任何偶数将印有2秒滞后而奇数数字将被立即打印。 反正运行它,我得到

1 
3 
5 
7 
9 

<<2 seconds break>> 

undefined 
undefined 
undefined 
undefined 
undefined 

看起来甚至值为“丢失”。 如何传递值以确保函数不会丢失输入值? 我错过了什么吗?

感谢, 莫罗

回答

0
setTimeout(function(i) { 
      console.log(i); 
     }, 2000); 

功能从setTimeout的叫不会与任何参数来调用。 您需要创建一个闭包。

检出this question和接受的答案。

(这不是一个特定的node.js-问题)

12

setTimeout参数函数被声明为取一个参数,i。当setTimeout像没有参数一样调用该函数时,参数因此被设置为undefined

这似乎是要稍好一些,因为它不再阴影外i变量原先尝试引用...

setTimeout(function() { 
    console.log(i); 
}, 2000) 

...但如果你运行它,你会发现它打印10 5次,因为您创建的每个函数都引用相同的i变量,当循环退出条件变为true并且它终止时,其值将为10

创建封闭持有的i的价值,因为它在每个setTimout参数函数创建将这样的伎俩在循环过程:

setTimeout((function(i) { 
    return function() { 
    console.log(i); 
    } 
})(i), 2000) 

重命名参数传递给Immediately-Invoked Function Expression我们只是用可能有助于让事情更清晰:

setTimeout((function(loopIndex) { 
    return function() { 
    console.log(loopIndex); 
    } 
})(i), 2000) 

我们:

  • 创建一个只接受一个参数并返回另一个函数的函数。
  • 立即调用“外部”函数,将当前值i(它不是一个对象,因此有效地传递值)传递给它。
  • “外部”函数返回“内部”函数,该函数作为参数传递给setTimeout

这样做是因为:

  1. 创建一个JavaScript函数创建一个新范围持有函数的参数变量和使用var关键字它内声明的任何其他变量。可以把它看作一个不可见的对象,其属性对应于变量名称。

  2. 所有功能都对其范围进行了定义,作为其范围链的一部分。即使它不再处于“活动状态”(例如,为返回创建的功能),它们仍然可以访问此范围。 “内部”函数创建于一个范围内,该范围包含一个loopIndex变量,在调用IIFE时设置为值i。因此,当您尝试从内部函数中引用名为loopIndex的变量时,它首先检查其自己的作用域(并且在那里找不到loopIndex然后开始向上遍历其作用域链 - 首先,它检查其中的作用域它被定义,其中确实包含loopIndex变量,其值被传递给console.log()

这就是一个闭包 - 一个函数可以访问定义的范围,即使范围的创建函数已经完成执行。

1

尽管您通常使用bind来确保回调函数内的this的值是您希望的值,但您也可以使用它来修改传递给绑定函数的参数。在你的情况下,代码可能看起来是这样的(我改变了一些变量的名称,以便它更清楚这是怎么回事):

for (var i = 0 ; i < 10 ; i++) { 
    if (i % 2 === 0) { 
    setTimeout(function(num) { 
     console.log(num); 
    }.bind(this, i), 2000); 
    } else { 
    console.log(i); 
    } 
} 

在这种情况下,该功能.bind(this, i)重视它。 this只是为了填充; bind的第一个参数始终是你想要的this解析为里面的的绑定函数。然后,我们可以传入想要添加到绑定函数参数中的任何值 - 在这种情况下,我们希望将当前值i(绑定函数时)传递给函数。

您可以通过MDN Docs了解有关bind的更多信息。