【问题标题】:Promise.all() not waiting for async processPromise.all() 不等待异步进程
【发布时间】:2018-02-07 04:43:44
【问题描述】:

在 node.js 中,我尝试遍历一些项目,为每个项目完成一个异步过程,然后等待每个项目完成,然后再开始下一个项目。我一定是做错了什么,因为 Promise.all() 没有等待任何异步进程完成!我的代码如下:

getChildLessons() {

 return new Promise((resolve, reject) => {

  Promise.all(

    //nested for loop is needed to get all information in object / arrays
   this.lessons.levels.map((item, i) => {

      item.childlevels.map((childItem, iChild) => {

        return ((i, iChild) => {

          //return async process with Promise.resolve();
          return this.horseman
          .open(childItem.url)
          .html()
          .then((html) => {
            //adding some information to an array
          })
          .then(() => {
              return Promise.resolve();
          }).catch((err) => {
            reject(err);
          });

        })(i, iChild);

      });
  })

  // Promise.all().then()
).then(() => {
  resolve(this.lesson);
})
.catch((err) => {
  console.log(err);
});
});
}

我对使用 node.js 进行异步还很陌生,所以如果可能的话,请你提供一个清晰的例子。

【问题讨论】:

  • map 是否返回承诺? Promise.all 期待承诺。
  • 嗨,迈克,我已经尝试将它包装在 Promise.resolve() 中,但我得到了相同的结果。
  • 首先,我建议你将 .all 部分的内部放入一个函数中,以便轻松查看发生了什么,并确保它返回一个可迭代的,即数组、对象等。在目前的方式中,它是很难理解会发生什么
  • 为什么不创建一个数组变量 (var myArray = []),然后将您的地图代码移出 Promise.all?然后,不要像现在这样返回每个承诺,而是将其推送到数组 (myArray.push(promise)) 中。然后将该数组传递给 Promise.all。通过这种方式,您可以验证您是否拥有 Promise.all 所期望的可迭代的 Promise。

标签: node.js asynchronous promise es6-promise


【解决方案1】:

需要解决两个问题才能使其正常工作:

  • 提供给外部map 调用的回调没有return 语句,因此map 创建了一个数组,其中所有元素都是undefined。你需要return child.items.map 的结果,即一组promise。
  • 一旦上述问题得到修复,外部map 将返回一个数组数组。这个 2D 的 promise 数组需要被展平为一个简单的 promise 数组。您可以使用 [].concat 和展开语法来做到这一点。

所以你的代码的第一行应该变成:

Promise.all(
    [].concat(...this.lessons.levels.map((item, i) => {
        return item.childlevels.map((childItem, iChild) => {

在适当的位置添加右括号——关闭concat(的参数列表。

其他一些评论:

  • 以下代码无用:

                  .then(() => {
                      return Promise.resolve();
                  })
    

调用.then 的promise 根据定义在调用回调的那一刻解决。在那一刻返回一个已解决的承诺不会增加任何有用的东西。返回调用 .then 的承诺就可以了。您可以从链中删除上述.then 调用。

  • 快结束时您致电resolve(this.lesson)。这是promise constructor anti pattern 的示例。您不应该创建新的 Promise,而是返回 Promise.all 调用的结果,并在其 .then 调用 return this.lesson 使其成为承诺的值。

链接所有的承诺

要链接所有承诺而不是使用Promise.all,最简单的方法是使用async/await 语法。像这样的:

async getChildLessons() {
    for (let item of this.lessons.levels) {
        for (let childItem of item.childlevels) {
            let html = await this.horseman
                                 .open(childItem.url)
                                 .html();
            //adding some information to an array
            // ...
        }
    }
    return this.lesson;
}

【讨论】:

  • 嗨,trincot,谢谢你的回答,它真的很清楚而且很有帮助!但是,您是否知道一种方法可以让数组中的每个 Promise 一个接一个地执行,而不是同时执行?
  • 查看我的答案。
【解决方案2】:

也许尝试做这样的事情?

let firstPromise = new Promise((resolve,reject) => {
    // your logic/code
})

//add more promises you want into array if u want


 Promise.all([firstPromise])
.then((response) => {
    //do stuff with response
})
.catch((error) => {
    //do stuff with error
})

【讨论】:

    【解决方案3】:

    下面的代码行没有返回任何东西。您正在尝试将未定义的数组传递给Promise.all

    this.lessons.levels.map((item, i) => {...})

    不过,您的代码还有几个问题。下面的块是完全没有必要的。它实际上什么都不做,只是在你的代码中添加一个额外的块

    return ((i, iChild) => {...})(i, iChild);

    您不需要从主函数返回PromisePromise.all() 的结果是 Promise

    考虑到以上,这里是一个代码sn-p。

    // an array of Promises
    var levelPromises = this.lessons.levels.map((item, i) => {
    
        var childPromises = item.childlevels.map((childItem, iChild) => {
            return this.horseman.open(childItem.url)
            //...
        })
    
        return Promise.all(childPromises)
    })
    
    return Promise.all(levelPromises)
    

    【讨论】:

      猜你喜欢
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-07
      • 2016-03-25
      • 2017-10-09
      相关资源
      最近更新 更多