asynchronous过程里面的JavaScript for循环

我正在运行以下forms的事件循环:

var i; var j = 10; for (i = 0; i < j; i++) { asycronouseProcess(callBackFunction() { alert(i); }); } 

我想要显示的是一系列显示数字0到10的警报。问题在于,当callback函数被触发时,循环已经经历了几次迭代,并且显示了更高的i值。 有关如何解决这个问题的任何build议?

当所有的asynchronous操作开始时, for循环立即运行完成。 当他们在将来完成一段时间并调用它们的callback时,循环索引variablesi的值将处于所有callback的最后值。

这是因为for循环不会等待asynchronous操作完成,然后再继续执行下一次循环,并且因为将来会调用asynchronouscallback。 因此,循环完成其迭代,然后在asynchronous操作完成时调用callback。 因此,循环索引被“完成”,并且处于所有callback的最终值。

要解决这个问题,你必须为每个callback单独保存循环索引。 在Javascript中,要做到这一点的方法是在函数闭包中捕获它。 要做到这一点,可以创build一个专门用于此目的的内联函数闭包(第一个示例如下所示),或者您可以创build一个外部函数,将索引传递给它并让它唯一地维护索引(第二个示例如下所示)。

截至2016年,如果您拥有一个完全符合规范的ES6 ES6实现,您还可以使用let来定义for循环variables,并将为for循环的每次迭代(下面的第三个实现)唯一地定义它。 但是,请注意,这是ES6实现中的一个延迟实现function,因此您必须确保您的执行环境支持该选项。

使用.forEach()来迭代,因为它创build了自己的函数闭包

 someArray.forEach(function(item, i) { asycronouseProcess(function(item) { console.log(i); }); }); 

使用IIFE创build您自己的函数closures

 var j = 10; for (var i = 0; i < j; i++) { (function(cntr) { // here the value of i was passed into as the argument cntr // and will be captured in this function closure so each // iteration of the loop can have it's own value asycronouseProcess(function() { console.log(cntr); }); })(i); } 

创build或修改外部函数并将其传递给variables

如果你可以修改asycronouseProcess()函数,那么你可以在那里传递值,并使asycronouseProcess()函数将asycronouseProcess()返回到callback函数中,如下所示:

 var j = 10; for (var i = 0; i < j; i++) { asycronouseProcess(i, function(cntr) { console.log(cntr); }); } 

使用ES6 let

如果你有一个完全支持ES6的Javascript执行环境,你可以在你的for循环中使用let

 const j = 10; for (let i = 0; i < j; i++) { asycronouseProcess(function() { console.log(i); }); } 

注意:此ES6function通常是JS引擎支持的后续ES6function之一,因此您必须确保在您的环境中支持此特定function。

有关如何解决这个问题的任何build议?

一些。 你可以使用bind :

 for (i = 0; i < j; i++) { asycronouseProcess(function (i) { alert(i); }.bind(null, i)); } 

或者,如果您的浏览器支持让 (它将在下一个ECMAScript版本,但Firefox已经支持一段时间),你可以有:

 for (i = 0; i < j; i++) { let k = i; asycronouseProcess(function() { alert(k); }); } 

或者,你可以做手动bind的工作(如果浏览器不支持,但我可以说你可以在这种情况下实现一个填充,它应该在上面的链接):

 for (i = 0; i < j; i++) { asycronouseProcess(function(i) { return function () { alert(i) } }(i)); } 

我通常更喜欢let我可以使用它(例如Firefox附加组件); 否则bind或自定义的curry函数(不需要上下文对象)。

async await在这里(ES7),所以你现在可以很容易地做这种事情。

  var i; var j = 10; for (i = 0; i < j; i++) { await asycronouseProcess(); alert(i); } 

请记住,这只有在asycronouseProcess返回一个Promise时才asycronouseProcess

如果asycronouseProcess不在你的控制之下,那么你可以让它自己像这样返回一个Promise

 function asyncProcess() { return new Promise((resolve, reject) => { asycronouseProcess(()=>{ resolve(); }) }) } 

然后把这行代替await asycronouseProcess(); 通过await asyncProcess();

理解Promises之前,甚至在查看async await是必须的 (也读了支持async await

JavaScript代码在单个线程上运行,所以在开始下一个循环之前,您不能主要等待第一个循环迭代,而不会严重影响页面的可用性。

解决scheme取决于你真正需要什么。 如果这个例子接近你所需要的,@ Simonbuild议把i传递给你的asynchronous过程是一个很好的例子。

 var i = 0; var length = 10; function for1() { console.log(i); for2(); } function for2() { if (i == length) { return false; } setTimeout(function() { i++; for1(); }, 500); } for1();