Promise.all() 不等待异步进程

6

在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吗?Promise.all方法需要传入Promise。 - Mike Cheel
嗨,Mike,我已经尝试将其包装在Promise.resolve()中,但结果相同。 - Atomicts
1
首先,我建议您将.all部分的内部放入一个函数中,以便轻松查看发生了什么,并确保它返回可迭代的内容,例如数组、对象等。以当前的方式,很难理解发生了什么。 - Mumin Korcan
2
为什么不创建一个数组变量(var myArray = []),然后将您的map(s)代码移出Promise.all呢?然后,不要像现在这样返回每个promise,而是将其推入数组中(myArray.push(promise))。然后将该数组传递给Promise.all。这样,您可以验证是否有一个可迭代的promise数组,这正是Promise.all所期望的。 - Mike Cheel
3个回答

6
需要解决两个问题才能使其正常工作:
  • 提供给外部 map 调用的回调函数没有 return 语句,因此导致 map 创建了一个所有元素均为 undefined 的数组。您需要返回 child.items.map 的结果,即一个 promise 数组。
  • 一旦上述问题得到解决,外部 map 将返回一个由数组组成的二维数组。这个包含 promises 的 2D 数组需要被展平成一个简单的 promise 数组。您可以使用 [].concat 和扩展语法实现这一点。
因此,您代码的开头应该如下所示:
Promise.all(
    [].concat(...this.lessons.levels.map((item, i) => {
        return item.childlevels.map((childItem, iChild) => {

在适当的位置添加一个闭括号,以关闭concat(的参数列表。

其他一些注意事项:

  • The following code is useless:

                  .then(() => {
                      return Promise.resolve();
                  })
    
.then 调用的 promise 在回调函数被调用时已经被解决,因此在那个瞬间返回一个解决的 promise 没有什么有用的添加。将 .then 中调用的 promise 返回就可以了。你可以将链中上述的 .then 调用删除。
在结尾附近你调用了 resolve(this.lesson)。这是使用promise 构造反面模式的实例。你不应该创建一个新的 Promise,而是应该返回 Promise.all 的结果,并在它的 .then 中使用 return this.lesson 使其成为承诺的值。
链接所有 promises 而不是使用 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,谢谢您的回答,非常清晰和有帮助!但是,您是否知道有没有一种方法可以使数组中的每个承诺一个接一个地执行,而不是同时执行所有承诺? - Atomicts
请看我回答的补充部分。 - trincot

0

或许可以尝试像这样做?

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

0
下面的代码行未返回任何内容。 您正试图将未定义的数组传递给Promise.all this.lessons.levels.map((item, i) => {...}) 但是,您的代码还有几个问题。 下面的块是完全不必要的。 它实际上除了向您的代码添加额外的块之外什么也没有做。 return ((i, iChild) => {...})(i, iChild); 您不需要从主函数中返回一个PromisePromise.all()的结果是一个Promise
考虑到以上问题,这里是一段代码片段。
// 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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接