我找到了一个使用 js 装饰器的优雅解决方案。 JS 装饰器仍然是stage two proposal。要使用它们,请添加
"ecmaFeatures": {
"legacyDecorators": true
}
到你的 eslint 配置。
然后你可以像这样使用装饰器:
实现装饰器:
function error_handler(target, property, descriptor){
let default_function = descriptor.value //The original function definition
descriptor.value = function () {
// The modified function
let result = default_function.call(this, ...arguments) // Call original function with object context and arguments
let do_default_error = true // Do default error handling ?
if (arguments.length > default_function.length){
// If the modified function is called with more arguments than the default function
// use the last argument to decide if we should do default error handling or not
do_default_error = Boolean(arguments[arguments.length])
}
if (do_default_error) {
return result.catch((error) => {
// The default error handling
error_notification(error)
throw error // Ensure promise chain
})
}else{
return result // Do nothing
}
}
return descriptor // return the modified function
}
在您的 api 定义中使用装饰器:
const pageService = {
@error_handler
getPage(pageId){
return doStuff(pageId) // doStuff must return a Promise
},
@error_handler
postPage(payload) {
return otherStuff()
}
}
利润
带有默认错误处理程序的简单 api 调用如下所示:
pageService.getPage(id).then((ok_result) => {foo()})
您仍然可以添加其他捕获 / finally 并且它们的行为符合预期
pageService.getPage(id).then((ok_result) => {foo()}).catch((error) => {
extraSteps()
}).finally(() => {cleanup()})
要禁用默认错误处理程序,请传递 false
pageService.getPage(id, false).then((ok_result) => {foo()}).catch((error) => {
doSpecialTreatement()
})
这种方法是我见过的最干净的方法。定义新的 api 函数很简单,用法也很简单。有一些缺点:
使用第二阶段提案。据我了解,babel 会处理这个问题。
您的代码中发生了一些神奇的事情。其他开发人员可能会感到困惑,为什么要传递一个额外的参数。您可以调整错误处理程序以始终使用最后一个参数并像这样定义您的 api 函数:
const pageService = {
@error_handler
getPage(pageId, doErrorHandling){
return doStuff(pageId)
}
这有点清楚,但您必须禁用 eslint 没有未使用的变量检查此文件。另一个想法是始终将对象传递给您的 api 函数并查找 doErrorHandling 属性。
免责声明:我不会认为自己是 js 专家。如果您喜欢这种方法,请自行判断。如果您发现任何严重的缺点,请发表评论。
感谢阅读:-)