javascript for循环中的异步进程

4szc88ey  于 2021-06-23  发布在  Mysql
关注(0)|答案(6)|浏览(448)

这个问题在这里已经有答案了

循环内的javascript闭包-简单实用示例(44个答案)
两年前关门了。
我正在运行以下形式的事件循环:

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

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

我正在尝试显示一系列显示数字0到10的警报。问题是,当回调函数被触发时,循环已经经历了一些迭代,并且它显示了一个更高的值 i . 关于如何解决这个问题有什么建议吗?

gmxoilav

gmxoilav1#

javascript代码在单个线程上运行,因此您不能在开始下一个循环迭代之前等待第一个循环迭代完成,而不会严重影响页面可用性。
解决办法取决于你真正需要什么。如果这个例子接近你所需要的,@simon建议通过 i 异步进程是一个很好的方法。

ix0qys7i

ix0qys7i2#

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

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

记住,只有在 asycronouseProcess 正在返回一个 Promise 如果 asycronouseProcess 如果不在你的控制范围内,那么你可以让它返回一个 Promise 像这样一个人

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

然后更换此线路 await asycronouseProcess();await asyncProcess(); 理解 Promises 在调查之前 async await 是必须的(同时阅读关于支持 async await )

cfh9epnr

cfh9epnr3#

es2017:您可以将异步代码 Package 到返回承诺(承诺中的异步代码)的函数(比如xhrpost)中。
然后调用for循环中的函数(xhrpost),但使用神奇的await关键字。:)

let http = new XMLHttpRequest();
let url = 'http://sumersin/forum.social.json';

function XHRpost(i) {
  return new Promise(function(resolve) {
    let params = 'id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo' + i + '&message=Here%20is%20the%20Demo&_charset_=UTF-8';
    http.open('POST', url, true);
    http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    http.onreadystatechange = function() {
    console.log("Done " + i + "<<<<>>>>>" + http.readyState);
          if(http.readyState == 4){
              console.log('SUCCESS :',i);
              resolve();
          }
         }
    http.send(params);       
    });
 }

(async () => {
    for (let i = 1; i < 5; i++) {
        await XHRpost(i);
       }
})();
368yc8dk

368yc8dk4#

这个 for 当所有异步操作启动时,循环立即运行到完成。当它们在将来某个时间完成并调用回调时,循环索引变量的值 i 对于所有回调都将是其最后一个值。
这是因为 for 循环不会等到异步操作完成后再继续循环的下一次迭代,因为异步回调将在将来某个时候调用。因此,循环完成其迭代,然后在异步操作完成时调用回调。因此,循环索引是“done”的,并且对于所有回调都位于其最终值。
为了解决这个问题,您必须为每个回调单独保存循环索引。在javascript中,这样做的方法是在函数闭包中捕获它。这可以通过专门为此目的创建一个内联函数闭包来实现(下面显示的第一个示例),也可以创建一个将索引传递给的外部函数,并让它为您唯一地维护索引(下面显示的第二个示例)。
从2016年起,如果您有一个完全符合规范的javascript es6实现,您还可以使用 let 定义 for 循环变量,它将为 for 循环(下面的第三个实现)。但是,请注意,这是es6实现中的一个后期实现特性,因此您必须确保您的执行环境支持该选项。
使用.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函数返回回调,如下所示:

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

使用es6 let 如果您有一个完全支持es6的javascript执行环境,那么可以使用 let 在你的 for 循环如下:

const j = 10;
for (let i = 0; i < j; i++) {
    asynchronousProcess(function() {
        console.log(i);
    });
}
``` `let` 在声明中声明 `for` 这样的循环声明将创建 `i` 对于循环的每次调用(这是您想要的)。
使用promises和async/await序列化
如果您的async函数返回一个承诺,并且您希望序列化您的async操作以一个接一个地运行,而不是并行运行,并且您正在支持 `async` 以及 `await` ,那么你有更多的选择。

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` .
并行运行异步操作并使用 `Promise.all()` 按顺序收集结果

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);
});

cs7cruho

cs7cruho5#

关于如何解决这个问题有什么建议吗?
好几个。您可以使用绑定:

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

或者,如果您的浏览器支持let(它将出现在下一个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 或者自定义currying函数(不需要上下文对象)。

pw9qyyiw

pw9qyyiw6#

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();

下面是一个示例函数方法,用于实现这里所期望的功能。

相关问题