然后用Javascript使用外部variables

我是新来的JavaScript,大多花了我的时间编写后端Java程序。 我无法弄清楚如何在调用时捕获范围variables的当前状态,例如: –

idList = [0, 1, 2, 3, 4, 5]; for(id in idList) { new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 300); }).then(function(num) { console.log(id + ': ' + num); return num * 2; }); } 

输出是: –

 "5: 10" "5: 10" "5: 10" "5: 10" "5: 10" "5: 10" 

但是我想要的是输出

 "0: 10" "1: 10" "2: 10" "3: 10" "4: 10" "5: 10" 

即想要然后捕获variablesid的当前状态。

这有几个解决scheme。

最简单的实现(给出你提供的代码),就是在你的for循环中放一个let之前的id。

 for (let id in idList) { ... } 

因为你使用的for...in循环是asynchronous的,所以在循环中包含的内部函数正在返回之前它已经完成了,因此id将始终是循环完成时(无论是5)的结果。

必须使用let的原因而不是var ,因为let语句声明了一个块作用域局部variables。

let允许你将范围受限的variables声明为使用它的块,语句或expression式。 这与var关键字不同,var关键字全局定义variables,或者在本地定义整个函数,而不考虑块范围。 ( 来源 )

所以, let工作吧, var (或者一个全局variables,就像你所拥有的那样)不会。

但是 ,当索引顺序很重要时,build议不要在数组的循环中使用for...in ( 来源 )


更好的解决scheme是使用for...of循环,它将以一致的顺序访问元素。 这也是一个相当简单的改变,用“of”replace“in”来创build循环。

 for (let id of idList) { ... } 

更具说明性的解决scheme是使用JavaScript中创build的每个数组上可用的forEach方法。 这是为数组中的每个元素执行callback函数。

 const idList = [0, 1, 2, 3, 4, 5]; idList.forEach(id => { new Promise((resolve, reject) => { setTimeout(() => resolve(10), 300); }) .then((num) => { console.log(id + ': ' + num); return num * 2; }); }) 

但是,请注意,即使您给callback一个返回值, forEach总是会返回undefined。 这在你的情况下似乎没有问题,因为你所要做的就是console.logasynchronous值。

由于循环内部的方法是asynchronous的,所以你必须使用:

IIFE

立即调用的函数expression式捕获当前状态,以便您的代码看起来像这样。

 for(var id in idList) { (function(_id){ new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 300); }).then(function(num) { console.log(_id + ': ' + num); return num * 2; }); }(id); } 

asynchronous模块

了解了IIFE之后,就可以开始使用轻松处理asynchronous方法的模块

使用这个,你的代码看起来像:

 async.each(idList, function(id, callback) { new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 300); }).then(function(num) { console.log(id + ': ' + num); callback(null, num * 2); return num * 2; }); }, function(err, results) { console.log('done with results %o', results); }); 

我希望这有帮助!

问题是for循环variables的范围。 for循环variables位于父函数或全局作用域中。 你可以在ES6中使用一个块范围的variables,比如let ,或者你需要通过将循环执行包装到一个函数中来创build一个新的variables范围。 您应该了解JavaScript范围和closures如何工作。

IMO最简单的解决scheme是:

  for(let id in idList) { new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 300); }).then(function(num) { console.log(id + ': ' + num); return num * 2; }); } 

但是,如果您正在严格的ES6环境中工作,则需要将循环执行上下文封装在一个函数中,以保留范围内的循环迭代器。

这是另一种方法

 idList = [0, 1, 2, 3, 4, 5]; idList.reduce((p, id) => { return p.then(function(id, value) { console.log("%s : %s", id, value); return new Promise(resolve => setTimeout(() => resolve(10), 300) ); }.bind(null, id)); }, Promise.resolve(10)); 

你可以试试这个

 idList = [0, 1, 2, 3, 4, 5]; for(id in idList) { new Promise(function(resolve, reject) { // A mock async action using setTimeout //save id value var idValue = id; setTimeout(function() { resolve({idValue: idValue, num: 10}); }, 300); }).then(function(obj) { console.log(obj.idValue + ': ' + obj.num); return obj.num * 2; }); } 

所以这里的问题是你在使用for...in并试图迭代数组。 这带来了id的范围的问题,当你的函数执行的时候, id的值已经是5

由于问题是在范围界定方面,正如@wrleskovec在评论中提到的,我们可以通过简单的改变来解决这个问题

 for (id in idList) 

 for (let id in idList) 

但是, 我们应该避免使用for...in来迭代数组 。

我们可以通过在数组中使用forEach封装id来解决这个问题。

 idList = [0, 1, 2, 3, 4, 5]; idList.forEach((id) => { new Promise(function(resolve, reject) { // A mock async action using setTimeout setTimeout(function() { resolve(10); }, 300); }).then(function(num) { console.log(id + ': ' + num); return num * 2; }); });