【问题标题】:Calling an asynchronous function within a for loop in JavaScript在 JavaScript 的 for 循环中调用异步函数
【发布时间】:2012-10-31 20:30:42
【问题描述】:

我有以下代码:

for(var i = 0; i < list.length; i++){
    mc_cli.get(list[i], function(err, response) {
        do_something(i);
    });
}

mc_cli 是与 memcached 数据库的连接。可以想象,回调函数是异步的,因此它可能会在 for 循环结束时执行。此外,当以这种方式调用 do_something(i) 时,它总是使用 for 循环的最后一个值。

我用这种方式尝试了闭包

do_something((function(x){return x})(i)) 

但显然这又总是使用 for 循环索引的最后一个值。

我也尝试在 for 循环之前声明一个函数,如下所示:

var create_closure = function(i) {
    return function() {
        return i;
    }
}

然后调用

do_something(create_closure(i)())

但同样没有成功,返回值始终是 for 循环的最后一个值。

谁能告诉我闭包我做错了什么?我以为我理解他们,但我不明白为什么这不起作用。

【问题讨论】:

    标签: javascript asynchronous for-loop closures


    【解决方案1】:

    由于您正在运行一个数组,您可以简单地使用forEach,它提供了列表项和回调中的索引。迭代将有自己的范围。

    list.forEach(function(listItem, index){
      mc_cli.get(listItem, function(err, response) {
        do_something(index);
      });
    });
    

    【讨论】:

    • @joseph 你的推理听起来很棒。你能解释一下我的这部分“迭代将有自己的范围”吗?
    • @SandipSubedi - 这就是 javascript 中的闭包概念发挥作用的地方。这是内部函数作用域记住其所有引用的能力,即使它们在函数作用域之外。您可以使用 console.dir(object) 在开发工具中查看闭包,或者只需在“do_something”函数处放置断点并查看开发工具的右窗格即可。您可以在此处了解更多信息 => developer.mozilla.org/en-US/docs/Web/JavaScript/Closures.
    【解决方案2】:

    这是循环内的异步函数范例,我通常使用立即调用的匿名函数来处理它。这可确保使用索引变量的正确值调用异步函数。

    好的,太好了。至此所有异步函数都已启动,循环退出。现在,由于它们的异步性质,或者它们将以什么顺序完成,这些函数何时完成,尚无定论。如果您的代码需要等到所有这些函数都完成后才能执行,我建议您简单地计算完成了多少函数:

    var total = parsed_result.list.length;
    var count = 0;
    
    for(var i = 0; i < total; i++){
        (function(foo){
            mc_cli.get(parsed_result.list[foo], function(err, response) {
                do_something(foo);
                count++;
                if (count > total - 1) done();
            });
        }(i));
    }
    
    // You can guarantee that this function will not be called until ALL of the
    // asynchronous functions have completed.
    function done() {
        console.log('All data has been loaded :).');
    }
    

    【讨论】:

      【解决方案3】:

      我知道这是一个旧线程,但无论如何添加我的答案。 ES2015let具有每次迭代都重新绑定循环变量的特性,所以在异步回调中保持循环变量的值,你可以试试下面的:

      for(let i = 0; i < list.length; i++){
          mc_cli.get(list[i], function(err, response) {
              do_something(i);
          });
      }
      

      但无论如何,最好使用forEach 或使用立即调用函数创建闭包,因为let 是 ES2015 功能,可能不支持所有浏览器和实现。从Bindings -&gt;let-&gt;for/for-in loop iteration scope 下的here 我可以看到直到 Edge 13 甚至直到 Firefox 49 (我还没有检查这些浏览器)才支持它。它甚至说 Node 4 不支持它,但我亲自测试过,它似乎是受支持的。

      【讨论】:

      • 过去 24 小时我一直在用头撞墙,因为我不明白为什么 f**king for loop 没有按预期工作。我一直在使用var i = 0,直到我看到你的帖子。我将var i = 0 更改为let i = 0,一切都神奇地工作正常。我怎么能给你我所有的名声,你应该得到这一切......
      • let 是我的赢家
      【解决方案4】:

      你已经很接近了,但你应该将闭包传递给get,而不是把它放在回调中:

      function createCallback(i) {
          return function(){
              do_something(i);
          }
      }
      
      
      for(var i = 0; i < list.length; i++){
          mc_cli.get(list[i], createCallback(i));
      }
      

      【讨论】:

        【解决方案5】:

        试试这个,使用async/await语法和Promise

        (async function() {
            for(var i = 0; i < list.length; i++){
                await new Promise(next => {
                    mc_cli.get(list[i], function(err, response) {
                        do_something(i); next()
                    })
                })
            }
        })()
        

        这将在每个循环中停止循环,直到触发next()函数

        【讨论】:

          【解决方案6】:

          ES2017:您可以将异步代码包装在一个函数中(比如 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);       
             });
          }
          
          for (let i = 1; i < 5; i++) {
              await XHRpost(i);
             }
          

          【讨论】:

            【解决方案7】:

            如果您想在循环中运行异步函数,但仍想在执行回调后保留索引或其他变量,您可以将代码包装在 IIFE(立即调用的函数表达式)中。

            var arr = ['Hello', 'World', 'Javascript', 'Async', ':)'];
            for( var i = 0; i < arr.length; i++) {
              (function(index){
                setTimeout(function(){
                   console.log(arr[index]);
             }, 500);
            

            【讨论】:

              【解决方案8】:
              (async function() {
                for(var i = 1; i < category.length; i++){
              
                 let result = await $.ajax({
                      url: '{{ route('admin.loyalty-commission.sub-category') }}', // Url of the Route
                      
                      data: {id:category[i-1], cat_state:i, next_cat:category[i]},
                      success: function (data) {
                          console.log('ajax success previous category id' + category[i-1]);
              
                          // Check if the logic was successful or not
                          if (data.status == 'success') {
                              
                              $(data.subcategories).appendTo(sub_category); //ajax result append to sub_category
                          } else {
                              console.log(data.msg);
                          }
                      },
                      error: function (data) {
                          // Error while calling the controller (HTTP Response Code different as 200 OK
                          console.log('Error:', data);
                          success = false;
                      }
                  });
               }
              })()
              

              【讨论】:

                【解决方案9】:

                这是来自我的应用程序的示例代码。它可能有助于解决问题。 我在地图循环中使用了异步/等待。多个 Promise 在一个数组中解析。

                这帮助我解决了这个问题 JavaScript async and await in loops

                 const refObjId= ['Account', 'Contact', 'Group'];
                
                 const readdirPro = file => {
                   return new Promise((resolve, reject) => {
                    fs.readdir(file, (err, data) => {
                      if (err) reject('I could not find the file');
                      resolve(data);
                    });
                  });
                };
                
                const fileNamePromises = refObjId.map(async el => {
                    const fileName = await readdirPro(`${__dirname}/../csv-files/${el}/data`);
                    return fileName;
                  });
                
                //fileNamePromises is an array of promises
                
                
                  const fileArr = await Promise.all(fileNamePromises);
                    console.log(fileArr);
                

                【讨论】:

                  【解决方案10】:

                  使用 ES6 (typescript) 你可以使用asyncawait 的好处:

                  let list: number[] = [1, 2, 3, 4, 5];
                  
                  // this is async fucntion
                  function do_something(counter: number): Promise<number> {
                      return new Promise((resolve, reject) => {
                          setTimeout(() => {
                              console.log('called after ' + counter + ' seconds');
                              resolve(counter);
                          }, counter * 1000);
                      })
                  }
                  async function foo() {
                      // itrate over list and wait for when everything is finished
                      let data = await Promise.all(list.map(async i => await do_something(i)));
                  
                      console.log(data);
                  }
                  
                  foo();
                  

                  【讨论】:

                  • “使用 ES6(打字稿)”,打字稿?? async/await 是语言的一部分!
                  猜你喜欢
                  • 2017-12-16
                  • 1970-01-01
                  • 2011-12-03
                  • 2014-07-09
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-09-11
                  • 2015-11-14
                  • 1970-01-01
                  相关资源
                  最近更新 更多