javascript for 循环中的异步进程 [英] Asynchronous Process inside a javascript for loop

查看:33
本文介绍了javascript for 循环中的异步进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am running an event loop of the following form:

var i;
var j = 10;
for (i = 0; i < j; i++) {

    asynchronousProcess(callbackFunction() {
        alert(i);
    });
}

我正在尝试显示一系列显示数字 0 到 10 的警报.问题是当回调函数被触发时,循环已经经历了几次迭代并且显示了更高的 i.有关如何解决此问题的任何建议?

I am trying to display a series of alerts showing the numbers 0 through 10. The problem is that by the time the callback function is triggered, the loop has already gone through a few iterations and it displays a higher value of i. Any recommendations on how to fix this?

推荐答案

for 循环会在所有异步操作开始时立即运行直至完成.当他们在未来的某个时间完成并调用他们的回调时,您的循环索引变量 i 的值将是所有回调的最后一个值.

The for loop runs immediately to completion while all your asynchronous operations are started. When they complete some time in the future and call their callbacks, the value of your loop index variable i will be at its last value for all the callbacks.

这是因为 for 循环在继续循环的下一次迭代之前不会等待异步操作完成,并且因为异步回调在未来某个时间被调用.因此,循环完成其迭代,然后在这些异步操作完成时调用回调.因此,循环索引完成"并处于所有回调的最终值.

This is because the for loop does not wait for an asynchronous operation to complete before continuing on to the next iteration of the loop and because the async callbacks are called some time in the future. Thus, the loop completes its iterations and THEN the callbacks get called when those async operations finish. As such, the loop index is "done" and sitting at its final value for all the callbacks.

要解决此问题,您必须为每个回调单独保存循环索引.在 Javascript 中,这样做的方法是在函数闭包中捕获它.这可以通过专门为此目的创建内联函数闭包来完成(下面显示的第一个示例),或者您可以创建一个外部函数,将索引传递给并让它为您唯一地维护索引(下面显示的第二个示例).

To work around this, you have to uniquely save the loop index separately for each callback. In Javascript, the way to do that is to capture it in a function closure. That can either be done be creating an inline function closure specifically for this purpose (first example shown below) or you can create an external function that you pass the index to and let it maintain the index uniquely for you (second example shown below).

截至 2016 年,如果您有一个完全符合 ES6 规范的 Javascript 实现,您还可以使用 let 来定义 for 循环变量,它会为 for 循环的每次迭代唯一定义(下面的第三个实现).但是,请注意,这是 ES6 实现中的一个后期实现功能,因此您必须确保您的执行环境支持该选项.

As of 2016, if you have a fully up-to-spec ES6 implementation of Javascript, you can also use let to define the for loop variable and it will be uniquely defined for each iteration of the for loop (third implementation below). But, note this is a late implementation feature in ES6 implementations so you have to make sure your execution environment supports that option.

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

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

使用 IIFE 创建自己的函数闭包

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
        asynchronousProcess(function() {
            console.log(cntr);
        });
    })(i);
}

创建或修改外部函数并将变量传递给它

如果您可以修改 asynchronousProcess() 函数,那么您可以将值传递到那里,并让 asynchronousProcess() 函数将 cntr 返回给回调,例如这个:

If you can modify the asynchronousProcess() function, then you could just pass the value in there and have the asynchronousProcess() function the cntr back to the callback like this:

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

使用 ES6 let

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

If you have a Javascript execution environment that fully supports ES6, you can use let in your for loop like this:

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

for 循环声明中声明的

let 像这样将为循环的每次调用创建一个唯一的 i 值(这就是你想要).

let declared in a for loop declaration like this will create a unique value of i for each invocation of the loop (which is what you want).

使用 promise 和 async/await 进行序列化

如果您的异步函数返回一个承诺,并且您希望序列化您的异步操作以依次运行而不是并行运行,并且您正在支持 async 的现代环境中运行>await,然后你有更多的选择.

If your async function returns a promise, and you want to serialize your async operations to run one after another instead of in parallel and you're running in a modern environment that supports async and await, then you have more options.

async function someFunction() {
    const j = 10;
    for (let i = 0; i < j; i++) {
        // wait for the promise to resolve before advancing the for loop
        await asynchronousProcess();
        console.log(i);
    }
}

这将确保一次只有一个对 asynchronousProcess() 的调用在进行中,并且 for 循环甚至在每个调用都完成之前不会推进.这与之前所有并行运行异步操作的方案不同,因此它完全取决于您想要的设计.注意:await 与承诺一起使用,因此您的函数必须返回一个在异步操作完成时已解决/拒绝的承诺.另外,请注意,为了使用 await,必须将包含函数声明为 async.

This will make sure that only one call to asynchronousProcess() is in flight at a time and the for loop won't even advance until each one is done. This is different than the previous schemes that all ran your asynchronous operations in parallel so it depends entirely upon which design you want. Note: await works with a promise so your function has to return a promise that is resolved/rejected when the asynchronous operation is complete. Also, note that in order to use await, the containing function must be declared async.

并行运行异步操作,使用Promise.all()按顺序收集结果

Run asynchronous operations in parallel and use Promise.all() to collect results in order

 function someFunction() {
     let promises = [];
     for (let i = 0; i < 10; i++) {
          promises.push(asynchonousProcessThatReturnsPromise());
     }
     return Promise.all(promises);
 }

 someFunction().then(results => {
     // array of results in order here
     console.log(results);
 }).catch(err => {
     console.log(err);
 });

这篇关于javascript for 循环中的异步进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆