【问题标题】:Best way to wait for .forEach() to complete等待 .forEach() 完成的最佳方式
【发布时间】:2016-07-16 01:41:08
【问题描述】:

有时我需要等待 .forEach() 方法完成,主要是在“加载器”功能上。我就是这样做的:

$q.when(array.forEach(function(item){ 
    //iterate on something 
})).then(function(){ 
    //continue with processing 
});

我不禁觉得这不是等待.forEach() 完成的最佳方式。最好的方法是什么?

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    如果forEach里面没有异步代码,那么forEach就不是异步的,比如这段代码:

    array.forEach(function(item){ 
        //iterate on something 
    });
    alert("Foreach DONE !");
    

    forEach 完成后您将看到警报。

    否则(你里面有异步的东西),你可以将forEach循环包装在Promise中:

    var bar = new Promise((resolve, reject) => {
        foo.forEach((value, index, array) => {
            console.log(value);
            if (index === array.length -1) resolve();
        });
    });
    
    bar.then(() => {
        console.log('All done!');
    });
    

    图片来源:@rolando-benjamin-vaz-ferreira

    【讨论】:

    • 如果循环内没有异步处理就可以了。
    • 警报将在 foreach 完成之前触发。它不起作用。
    • 未添加异步。另外(当循环中存在异步代码时),这不能保证最后一个项目是否首先完成,因此即使在所有线程/项目完成之前,resolve() 也会发生。
    • 如果循环内有异步代码则不起作用。
    • 这样不行,forEach循环不保证按顺序运行,所以当index === array.length检查通过时,不能保证所有的item都处理完了.我认为@PJ3 答案是最简单且有效的。
    【解决方案2】:

    使用 ES6 完成这项工作的最快方法就是使用 for..of 循环。

    const myAsyncLoopFunction = async (array) => {
      const allAsyncResults = []
    
      for (const item of array) {
        const asyncResult = await asyncFunction(item)
        allAsyncResults.push(asyncResult)
      }
    
      return allAsyncResults
    }
    

    或者您可以像这样使用Promise.all() 并行循环所有这些异步请求:

    const myAsyncLoopFunction = async (array) => {
      const promises = array.map(asyncFunction)
      await Promise.all(promises)
      console.log(`All async tasks complete!`)
    }
    

    【讨论】:

    • Promise.all() 版本必须是 const myAsyncLoopFunction = async (array) => {const myAsyncLoopFunction = async function (array) { 才能使其成为函数!
    • 感谢@AdamMarsh。在答案中编辑。
    • 一个解释会让这很有用。哪个部分是功能?如何将数组的每一项传递给外部函数?
    • 第 5 行 asnycResult 而不是 asyncResult 中有错字:)
    • @Taulant 修复了它
    【解决方案3】:
    var foo = [1,2,3,4,5,6,7,8,9,10];
    

    如果您实际上是在循环中执行异步操作,则可以将其包装在一个 Promise 中...

    var bar = new Promise((resolve, reject) => {
        foo.forEach((value, index, array) => {
            console.log(value);
            if (index === array.length -1) resolve();
        });
    });
    
    bar.then(() => {
        console.log('All done!');
    });
    

    【讨论】:

    • 这将错误地解决承诺,例如,如果具有键 1 的元素比具有键 2 的元素需要更长的时间来处理。
    • 这不能正常工作!正如@akrz 所说,这仅适用于具有最高索引的承诺也需要最长的时间。可能永远不会是这样的。
    【解决方案4】:

    如果您在循环中有一个异步任务并且您想等待。你可以使用for await

    for await (const i of images) {
        let img = await uploadDoc(i);
    };
    
    let x = 10; //this executes after
    

    【讨论】:

      【解决方案5】:

      forEach() 不返回任何内容,因此更好的做法是 map() + Promise.all()

      var arr = [1, 2, 3, 4, 5, 6]
      
      var doublify = (ele) => {
        return new Promise((res, rej) => {
          setTimeout(() => {
              res(ele * 2)
          }, Math.random() ); // Math.random returns a random number from 0~1
        })
      }
      
      var promises = arr.map(async (ele) => {
        // do some operation on ele
        // ex: var result = await some_async_function_that_return_a_promise(ele)
        // In the below I use doublify() to be such an async function
      
        var result = await doublify(ele)
        return new Promise((res, rej) => {res(result)})
      })
      
      Promise.all(promises)
      .then((results) => {
        // do what you want on the results
        console.log(results)
      })
      

      【讨论】:

      • @RameshPareek 嗨,我认为你可以使用基本的 for 循环 + await 来完成 Promise 的顺序解析
      • 但是 OP 询问 foreach 不是为了! :)
      • 解决方案适用于 for-each 中的异步任务。非常感谢!
      【解决方案6】:

      使用for of 代替forEach。像这样:

      for (const item of array) {
        //do something
      }
      console.log("finished");
      

      finished”将在循环结束后被记录。

      【讨论】:

        【解决方案7】:

        确保所有 forEach() 元素都完成执行的通用解决方案。

        const testArray = [1,2,3,4]
        let count = 0
        
        await new Promise( (resolve) => {
          testArray.forEach( (num) => {
            try {
              //some real logic
              num = num * 2
            } catch (e) {
              // error handling
              console.log(e)
            } fanally {
              // most important is here
              count += 1
              if (count == testArray.length) {
                resolve()
              }
            }
          })  
        })

        这个想法与使用索引计数的答案相同。但在实际情况下,如果发生错误,则索引方式无法正确计数。所以解决方案更加健壮。

        谢谢

        【讨论】:

        • 这应该是公认的答案,因为这也保证了循环内所有项目的执行。
        【解决方案8】:

        在每个可能的唯一代码分支(包括回调)末尾更改并检查计数器。示例:

        const fs = require('fs');
        
        /**
         * @description Delete files older than 1 day
         * @param {String} directory - The directory to purge
         * @return {Promise}
         */
        async function purgeFiles(directory) {
          const maxAge = 24*3600000;
          const now = Date.now();
          const cutoff = now-maxAge;
        
          let filesPurged = 0;
          let filesProcessed = 0;
          let purgedSize = 0;
        
          await new Promise( (resolve, reject) => {
            fs.readdir(directory, (err, files) => {
              if (err) {
                return reject(err);
              }
              if (!files.length) {
                return resolve();
              }
              files.forEach( file => {
                const path = `${directory}/${file}`;
                fs.stat(path, (err, stats)=> {
                  if (err) {
                    console.log(err);
                    if (++filesProcessed === files.length) resolve();
                  }
                  else if (stats.isFile() && stats.birthtimeMs < cutoff) {
                    const ageSeconds = parseInt((now-stats.birthtimeMs)/1000);
                    fs.unlink(path, error => {
                      if (error) {
                        console.log(`Deleting file failed: ${path} ${error}`);
                      }
                      else {
                        ++filesPurged;
                        purgedSize += stats.size;
                        console.log(`Deleted file with age ${ageSeconds} seconds: ${path}`);
                      }
                      if (++filesProcessed === files.length) resolve();
                    });
                  }
                  else if (++filesProcessed === files.length) resolve();
                });
              });
            });
          });
        
          console.log(JSON.stringify({
            directory,
            filesProcessed,
            filesPurged,
            purgedSize,
          }));
        }
        
        // !!DANGER!! Change this line! (intentional syntax error in ,')
        const directory = ,'/tmp'; // !!DANGER!! Changeme
        purgeFiles(directory).catch(error=>console.log(error));
        

        【讨论】:

          【解决方案9】:
          const array = [1, 2, 3];
          const results = [];
          let done = 0;
          
          const asyncFunction = (item, callback) =>
            setTimeout(() => callback(item * 10), 100 - item * 10);
          
          new Promise((resolve, reject) => {
            array.forEach((item) => {
              asyncFunction(item, (result) => {
                results.push(result);
                done++;
                if (done === array.length) resolve();
              });
            });
          }).then(() => {
            console.log(results); // [30, 20, 10]
          });
          
          // or
          // promise = new Promise(...);
          // ...
          // promise.then(...);
          

          “results”数组中结果的顺序可能与原始数组中的项顺序不同,具体取决于每个项的 asyncFunction() 完成的时间。

          【讨论】:

            【解决方案10】:

            我不确定这个版本与其他版本相比的效率,但我最近在我的 forEach() 中有一个异步函数时使用了它。它不使用承诺、映射或 for-of 循​​环:

            // n'th triangular number recursion (aka factorial addition)
            function triangularNumber(n) {
                if (n <= 1) {
                    return n
                } else {
                    return n + triangularNumber(n-1)
                }
            }
            
            // Example function that waits for each forEach() iteraction to complete
            function testFunction() {
                // Example array with values 0 to USER_INPUT
                var USER_INPUT = 100;
                var EXAMPLE_ARRAY = Array.apply(null, {length: USER_INPUT}).map(Number.call, Number) // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, n_final... ] where n_final = USER_INPUT-1
            
                // Actual function used with whatever actual array you have
                var arrayLength = EXAMPLE_ARRAY.length
                var countMax = triangularNumber(arrayLength);
                var counter = 0;
                EXAMPLE_ARRAY.forEach(function(entry, index) {
                    console.log(index+1); // show index for example (which can sometimes return asynchrounous results)
            
                    counter += 1;
                    if (triangularNumber(counter) == countMax) {
            
                        // function called after forEach() is complete here
                        completionFunction();
                    } else {
                        // example just to print counting values when max not reached
                        // else would typically be excluded
                        console.log("Counter index: "+counter);
                        console.log("Count value: "+triangularNumber(counter));
                        console.log("Count max: "+countMax);
                    }
                });
            }
            testFunction();
            
            function completionFunction() {
                console.log("COUNT MAX REACHED");
            }

            【讨论】:

              【解决方案11】:

              我不得不处理同样的问题(forEach 使用 multiple promises inside)并且当前提出的解决方案都对我没有帮助。所以我实现了一个检查数组,每个承诺都会更新其完整状态。我们有一个包含整个过程的一般承诺。我们仅在每个承诺完成时才解决一般承诺。片段代码:

              function WaitForEachToResolve(fields){
              
                  var checked_fields = new Array(fields.length).fill(0);
                  const reducer = (accumulator, currentValue) => accumulator + currentValue;
              
                  return new Promise((resolve, reject) => {
              
                    Object.keys(fields).forEach((key, index, array) => {
              
                      SomeAsyncFunc(key)
                      .then((result) => {
              
                          // some result post process
              
                          checked_fields[index] = 1;
                          if (checked_fields.reduce(reducer) === checked_fields.length)
                              resolve();
                      })
                      .catch((err) => {
                          reject(err);
                      });
                    }
                  )}
              }
              

              【讨论】:

                【解决方案12】:

                我喜欢使用 async-await 而不是 .then() 语法所以对于数据的异步处理,修改了@的答案罗纳尔多这样 -

                let finalData = [];
                var bar = new Promise(resolve => {
                    foo.forEach((value, index) => {
                        const dataToGet = await abcService.getXyzData(value);
                        finalData[index].someKey = dataToGet.thatOtherKey;
                        // any other processing here
                        if (finalData[dataToGet.length - 1].someKey) resolve();
                    });
                });
                
                await Promise.all([bar]);
                console.log(`finalData: ${finalData}`);
                

                注意我已经修改了 if 条件,它解决了满足我的条件的承诺。你可以在你的情况下做同样的事情。

                【讨论】:

                  【解决方案13】:

                  您可以使用它,因为我们在 forEach 循环中使用 async/await。您可以在循环中使用自己的逻辑。

                      let bar = new Promise((resolve, reject) => {
                          snapshot.forEach(async (doc) => {
                              """Write your own custom logic and can use async/await
                              """
                              const result = await something()
                              resolve(result);
                          });
                      });
                      let test = []
                      test.push(bar)
                      let concepts = await Promise.all(test);
                      console.log(concepts);
                  

                  【讨论】:

                    【解决方案14】:

                    对于简单的比较代码,我喜欢使用 for 语句。

                    doit();
                    function doit() {
                    
                            for (var i = 0; i < $('span').length;  i++) {
                                console.log(i,$('span').eq(i).text() );
                                if ( $('span').eq(i).text() == "Share a link to this question"  ) { //  span number 59
                                    return;
                                }
                            }
                    
                    alert('never execute');
                    
                    }
                    

                    【讨论】:

                      【解决方案15】:

                      我一直在用这个,效果最好.forEach()

                      //count
                      var expecting = myArray.length;
                      
                      myArray.forEach(function(item){
                      
                      //do logic here
                      var item = item
                      
                      
                      
                      //when iteration done
                      if (--expecting === 0) {
                      
                      console.log('all done!');
                      
                      }
                      
                      })
                      

                      【讨论】:

                        猜你喜欢
                        • 2015-02-23
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-04-23
                        • 1970-01-01
                        • 2020-04-15
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多