如何在不破坏 Promise 链的情况下处理 Bluebird 中的拒绝?

How to handle rejections in Bluebird without breaking a Promise chain?

假设我正在构建一个典型的 RSS-reader。我正在解析多个提要并将它们的所有剧集写入数据库:

const processEpisode = (episode)=>
  fetchMetadata(episode)
  .then(parseMetadata)
  .then(writeToDb)

const processFeed = (feed)=>
  fetchEpisodes(feed) // returns [Episode]
  .map(processEpisode,  { concurrency: 3 })

// main
getFeeds() // returns [Feed]
.map(processFeed, { concurrency: 3 })
.catch(err=> console.log(err))
  1. 我们得到所有的提要
  2. 对于每个提要发出 processFeed()
  3. 对于每一集,从 processFeed()
  4. 发出 processEpisode()

然而,如果 fetchMetadata(episode) 对于某些 feed 的某些片段抛出拒绝,所有 链被打破并立即落入全局 .catch(err=> console.log(err))

正常情况下我们需要对未处理的情节进行处理,但至少应该正常处理。一种解决方案是将 processEpisode() 包装在外部 Promise 中并就地处理。

const processEpisode = (episode)=>
  new Promise((resolve, reject)=> {
    fetchMetadata(episode)
    .then(parseMetadata)
    .then(writeToDb)
    .then((result)=> resolve(result))
    .catch((err)=> {
      // something bad happened
      // process and error, but resolve a fullfilled Promise!
      resolve(true)
    })
  })

但是,我想这是一个明显的反模式。如果在 processEpisode() 之后更高级别的 Promise 链中有另一个元素,它将失败,因为 processEpisode 将解析 true 而不是实际结果。

有没有优雅的方法可以解决这样的问题?我查看了 Bluebird 中的 finally 语句,但我不确定这是最好的方法。

谢谢!

只需将 .catch() 处理程序直接放在 processFeed() 上,这样您就可以在本地处理拒绝并将其转化为已解决的承诺,这将允许其他一切继续进行:

// main
getFeeds() // returns [Feed]
.map(function(item, index, length) {
    return processFeed(item, index, length).catch(function(reason) {
       // do whatever you want to here, this will "handle" the rejection
       //   and turn it into a resolved promise
       // whatever you return here will become the resolved value
    });
}, { concurrency: 3 })
.catch(err=> console.log(err))

注意:您不需要额外的包装承诺。添加一个 .catch() 处理程序并 return 从 .catch() 处理程序中获取一个正常值会将被拒绝的承诺变成已解决的承诺,因为此时拒绝被视为 "handled"。 .catch() 处理程序中 return 的任何值都会成为父 promise 的解析值。

.catch() 处理程序只有在return是一个被拒绝的承诺或抛出异常时才会保持被拒绝的承诺。