如何在不破坏 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))
- 我们得到所有的提要
- 对于每个提要发出
processFeed()
- 对于每一集,从
processFeed()
发出 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是一个被拒绝的承诺或抛出异常时才会保持被拒绝的承诺。
假设我正在构建一个典型的 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))
- 我们得到所有的提要
- 对于每个提要发出
processFeed()
- 对于每一集,从
processFeed()
发出
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是一个被拒绝的承诺或抛出异常时才会保持被拒绝的承诺。