一旦所有嵌套的承诺都解决了

时间:2017-01-28 02:43:26

标签: javascript asynchronous recursion promise

我正在尝试使用他们的Firebase API以递归方式获取黑客新闻故事的所有评论。故事具有kids属性,该属性是表示注释的ID数组。每个注释都可以有自己的kids属性,指向其子注释,依此类推。我想创建一个整个注释树的数组,看起来像:

[{
  'title': 'comment 1', 
  'replies': [{
    'title': 'comment 1.1'
  }, {
    'title': 'comment 1.2'
    'replies': [{
      'title': 'comment 1.2.1'
    }]
  }]
}]

我以为我可以使用以下功能执行此操作:

function getItem(id) {
    return api
        .child(`item/${id}`)
        .once('value')
        .then((snapshot) => {  // this is a Firebase Promise
            let val = snapshot.val()

            if (val.kids) {
                val.replies = val.kids.map((id) => getItem(id))
            }

            return val
        })
}

然后在使用以下内容获取整个评论树后收到通知:

getItem(storyId)
    .then(story => {
      // The story and all of its comments should now be loaded
      console.log(story)
    })

最终发生的事情是Promise.all().then()在第一级评论承诺解决后触发(这是有道理的,因为所有commentPromises已经解决了。)但是,我想知道所有嵌套承诺已经解决。我怎么能这样做?

2 个答案:

答案 0 :(得分:7)

在其他答案中不需要包装器承诺;这就是“explicit promise constructor anti-pattern”。精简一点,您可以执行以下操作:

function getItem(id) {
  return api
    .child(`item/${id}`)
    .once('value')
    .then(snapshot => {
      const val = snapshot.val();
      return Promise.all((val.kids || []).map(getItem))
        .then(kidsVals => {
          val.replies = kidsVals; 
          return val; 
        });
      );
    });
}

也不需要任何明确的拒绝处理。拒绝会自然地传播到顶层(假设这是你想要的)。

答案 1 :(得分:3)

IMP:请参阅azaburo的回答。它比我的要好得多。

======

我认为这应该有效:

function getItem(id) {
    return new Promise(function(resolve, reject) {
        api
        .child(`item/${id}`)
        .once('value')
        .then((snapshot) => {  // this is a Firebase Promise
            let val = snapshot.val()

            if (val.kids) {
                let replies = val.kids.map((id) => getItem(id))
                Promise.all(replies).then(replies => {
                    val.replies = replies // <<<<<< try this
                    resolve(val) // we want to resolve val, not val.replies
                }, err =>{
                    reject(err)
                })
            } else {
                resolve(val)
            }
        })
        .catch((err) => { // if there was some error invoking api call we need to reject our promise
            reject(err);
        })
    }
}

修改val.replies

中设置then