【问题标题】:How to use promise in forEach loop of array to populate an object如何在数组的forEach循环中使用promise来填充对象
【发布时间】:2016-11-16 15:56:26
【问题描述】:

我在一个数组上运行一个 forEach 循环并进行两次返回承诺的调用,我想填充一个对象,比如this.options,然后用它做其他事情。现在,如果我使用以下代码示例并首先进入 then 函数,我会遇到异步问题。

$.when.apply($, someArray.map(function(item) {
    return $.ajax({...}).then(function(data){...});
})).then(function() {
    // all ajax calls done now
});

这是下面的工作代码,但它仅适用于数组中的第一个元素,因为我在响应的 .then 中调用了结果函数。我想先对数组的所有元素进行所有提取,然后调用结果函数来做某事。

array.forEach(function(element) {
    return developer.getResources(element)
        .then((data) = > {
            name = data.items[0];
            return developer.getResourceContent(element, file);
        })
        .then((response) = > {
            fileContent = atob(response.content);
            self.files.push({
                fileName: fileName,
                fileType: fileType,
                content: fileContent
            });
            self.resultingFunction(self.files)
        }).catch ((error) = > {
            console.log('Error: ', error);
        })
});

如何在 forEach 循环完成后填充 self.files 对象,然后使用 files 对象调用生成的函数?

【问题讨论】:

  • 当您明确需要map 时,为什么还要forEach
  • @YuryTarabanko 我用了一张地图,但也没有用。我也尝试了答案中的方法,但使用了$.when.apply($, promises).then(),但也没有用。我认为它不起作用,因为我没有按照 ES6 的方式来做。

标签: javascript arrays foreach promise es6-promise


【解决方案1】:

Promise.all() 在这里会有所帮助:

var promises = [];

array.forEach(function(element) {
    promises.push(
        developer.getResources(element)
            .then((data) = > {
                name = data.items[0];
                return developer.getResourceContent(element, file);
            })
            .then((response) = > {
                fileContent = atob(response.content);
                self.files.push({
                    fileName: fileName,
                    fileType: fileType,
                    content: fileContent
                });
            }).catch ((error) = > {
                console.log('Error: ', error);
            })
    );
});

Promise.all(promises).then(() => 
    self.resultingFunction(self.files)
);

这将为每个项目启动 AJAX 调用,一旦调用完成,将每次调用的结果添加到 self.files,并在所有调用完成后调用 self.resultingFunction()

编辑:根据 Yury Tarabanko 的建议进行了简化。

【讨论】:

  • 应该是Promise.all,而不是Promises(复数)。
  • 为什么要手动创建 Promise?看起来getResources 已经返回一个
  • @YuryTarabanko 出于好奇,如果您将从getResources 返回的承诺直接推送到promises 数组而不是将其包装在您自己的数组中,是否可以保证调用所有单独的终结处理程序首先在Promise.all() 的终结处理程序之前?如果没有,创建自己的 Promise 似乎是必要的。
  • @TimoSta 不是直接来自getResources,而是你在链中获得的最后一个承诺getResources().then(fn1).then(fn2).catch()。每次您致电then,您都会得到一个新的承诺。并且确定fn1 将在fn2 之前和终结处理程序之前被调用。很简单,因为 promises 会将 fn1 的结果传递给 fn2,然后传递给终结处理程序(作为相应的数组元素)。
  • 啊,我明白了。今天学到了新东西!谢谢:-)
【解决方案2】:

上面接受的解决方案只是略有不同:

var promises = array.map(function(element) {
      return developer.getResources(element)
          .then((data) = > {
              name = data.items[0];
              return developer.getResourceContent(element, file);
          })
          .then((response) = > {
              fileContent = atob(response.content);
              self.files.push({
                  fileName: fileName,
                  fileType: fileType,
                  content: fileContent
              });
          }).catch ((error) = > {
              console.log('Error: ', error);
          })
});

Promise.all(promises).then(() => 
    self.resultingFunction(self.files)
);

我使用Array.map而不是Array.forEach,这意味着我不需要先创建一个空数组,我只是重新使用现有的。

【讨论】:

  • 您能否突出显示您所做的更改,以便我们不必阅读每一行?
  • 在末尾添加了一个新句子来解释我做了什么。
【解决方案3】:

您可以使用.map 作为一个循环来启动对数组每个元素的 Promise 请求,并返回这些 Promise 的新数组。 我也使用了解构,但不确定我们是否需要它。

await Promise.all([...arr.map(i => "our promise func"())]);

【讨论】:

  • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
【解决方案4】:

您可以查看this answer to a similar question 以获得极好的提示。那里给出的解决方案使用Array#reduce() 来避免在做任何工作之前必须积累所有的承诺,而不是使用Promise.all()

【讨论】:

    【解决方案5】:

    以下代码是使用 Promise 对同步的简单理解。

    let numArr = [1,2,3,4,5];
    let nums=[];
    
    let promiseList = new Promise(function(resolve,reject){
      setTimeout(()=>{
            numArr.forEach((val)=>{
            nums.push(val);
        });
        resolve(nums);
     },5000)
    })
    
    
    Promise.all([promiseList]).then((arrList)=>{
      arrList.forEach((array)=>{
        console.log("Array return from promiseList object ",array);    
      })
    
     });
    

    以上示例将数组 nums 保存 5 秒。发布后将在控制台上打印。

    【讨论】:

      猜你喜欢
      • 2020-03-02
      • 2018-04-06
      • 1970-01-01
      • 2021-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多