我现在正在用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 :(得分:12)
您的setTimeout
参数函数被声明为采用单个参数i
。当setTimeout
调用没有参数的函数时,参数设置为undefined
。
这似乎会稍好一些,因为它不再会影响您最初尝试引用的外部i
变量...
setTimeout(function() {
console.log(i);
}, 2000)
...但是如果你运行它,你会发现它打印10
5次,因为你创建的每个函数都引用相同的i
变量,其值为{{1}当循环退出条件变为10
并且它终止时。
创建一个闭包,其中包含true
的值,就像在创建每个i
参数函数的循环中一样,这样做可以解决问题:
setTimout
将参数重命名为我们刚刚使用的Immediately-Invoked Function Expression可能有助于使事情更清晰:
setTimeout((function(i) {
return function() {
console.log(i);
}
})(i), 2000)
我们是:
setTimeout((function(loopIndex) {
return function() {
console.log(loopIndex);
}
})(i), 2000)
的当前值(不是对象,因此通过值有效传递)。i
。这是因为:
在JavaScript中创建一个函数会创建一个新的范围来保存函数的参数变量以及使用setTimeout
关键字在其中声明的任何其他变量。可以把它想象成一个具有与变量名对应的属性的不可见对象。
所有函数都包含对其定义范围的引用,作为范围链的一部分。他们仍然可以访问此范围,即使它不再“活动”(例如,当它为返回创建它的功能时)。 “内部”函数是在包含var
变量的范围中创建的,在调用IIFE时设置为loopIndex
的值。因此,当您尝试从内部函数内部引用名为i
的变量时,它首先检查自己的范围(并且在那里找不到loopIndex
)然后开始行走它的范围链 - 首先,它检查它的定义范围, 包含loopIndex
变量,其值传递给loopIndex
。
这就是闭包 - 一个可以访问定义范围的函数,即使创建范围的函数已经完成执行。
答案 1 :(得分: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
的更多信息。
答案 2 :(得分:0)
setTimeout(function(i) {
console.log(i);
}, 2000);
不会使用任何参数调用从setTimeout调用的函数。 你需要创建一个闭包。
查看this question和接受的答案。
(这不是node.js特定的问题)