async function API(){
return Promise.resolve({
status: 200,
data: {
id: 'xxx'
}
})
}
function status(v: any){
if(v.status === 404){
return Promise.reject('404')
}
return v
}
function getData(v: any){
if(!v.data){
return Promise.reject('data is undefined')
}
return v.data
}
async function load(){
const d = await API().then(status).then(getData)
console.log(d)
}
load()
这里模拟一个简单的请求过程, 实际开发中除了对数据做业务处理外。 还需要做异常处理。全局错误,中间件错误,本地错误等
错误捕获
模式一
API().then(status).then(getData).catch((e) => {
if(e === '404'){ ... }
if(e === 'data is undefined'){...}
...
})
将错误处理放在所有处理之后,这种模式对于需要处理全局错误时,会产生大量模板代码,且如果需要处理的错误类型比较多的话。处理函数体积将变得比较臃肿,一些不相关的逻辑混杂在一起
模式二
API()
.then(status)
.catch((e) => {
if(!!e && e === '404'){
....
return Promise.reject(null)
}
return Promsie.reject(e)
})
.then(getData)
.catch((e) => {
if(!!e && e === 'data is undefined'){
...
return Promise.reject(e)
}
return Promise.reject(e)
})
为可能报错的处理段,配置对应的错误捕获。这里有利于拆分不同的错误处理逻辑。但由于Promise不存在中断处理,当前错误捕获后依然会处罚后续逻辑, 所以我们依然需要在每个错误处理中添加错误类型判断。
Promise 反模式
其实大部分情况下,我需要的是一个只针对当前错误的处理模式。进一步的话,就是函数只捕获自身可处理的错误. 不能处理的错误跳过直接向下传递。 这就有点像python的处理方式了
错误代理
API()
.then(status)
.catch(statusError, '404')
.then(getData)
.catch(getDataError, 'data is undefined')
这里用反模式的方式实现类似的功能
type IPromiseSig = Symbol | string | number | Error
type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? U : never
interface Promise<T>{
customCatch(cb: Function, filter: (d: any) => boolean): Promise<T>
ignore(cb: Function, sig?: IPromiseSig | IPromiseSig[]):Promise<T>
capture(cb: Function, sig?: IPromiseSig | IPromiseSig[]):Promise<T>
}
将具体的方法挂载在Promise原型上
// 默认错误标识
const PROMISE_CANCEL: Symbol = Symbol('cancel')
// 忽略指定错误类型
Promise.prototype.ignore = function(cb: Function, sig: IPromiseSig | IPromiseSig[] = PROMISE_CANCEL){
return this.catch((e: any) => {
const sigs = Array.isArray(sig) ? sig : [sig]
return sigs.includes(e) ? Promise.reject(e) : cb(e)
})
}
// 捕获指定错误类型
Promise.prototype.capture = function(cb: Function, sig?: IPromiseSig | IPromiseSig[]){
return this.catch((e: any) => {
const sigs = Array.isArray(sig) ? sig : [sig]
return sigs.includes(e) ? cb(e) : Promise.reject(e)
})
}
使用例子
// ignore
function test1(e: string){
Promise.reject(e)
.then(
() => console.log('1')
)
.ignore(
// 忽略 Error 错误
(e: any) => console.log('1', e),
[400]
)
.ignore(
(e: any) => console.log('2', e),
[404]
)
.catch(
// 捕获任意错误
(e: any) => console.log(e)
)
}
test1(400)
// 2 400
test1(404)
// 1 404
// capture
function test2(e: any){
Promise.reject(e)
.then(
() => console.log('1')
)
.capture(
// 忽略 Error 错误
(e: any) => console.log('1', e),
[400]
)
.capture(
(e: any) => console.log('2', e),
[404]
)
.catch(
// 捕获任意错误
(e: any) => console.log(e)
)
}
test2(400)
// 1 400
test2(404)
// 2 404
test2(500)
// 500
配合 async await
针对async 做简单处理
interface Promise<T>{
...
capture(cb: Function, sig?: IPromiseSig | IPromiseSig[]):Promise<T>
to():Promise<[T, null] | [null, Error]>
ignoreTo(sig?: IPromiseSig | IPromiseSig[]):Promise<[T, null] | [null, Error]>
captureTo(sig?: IPromiseSig | IPromiseSig[]):Promise<[T, null] | [null, Error]>
}
Promise.prototype.to = function(){
return this.then((d: PromiseType<typeof this>) => [d, null] as [PromiseType<typeof this>, null])
.catch((e: Error) => [null, e] as [null, Error])
}
Promise.prototype.ignoreTo = function(sigs?: IPromiseSig | IPromiseSig[]){
return this.then((d: PromiseType<typeof this>) => [d, null] as [PromiseType<typeof this>, null])
.ignore((e: Error) => [null, e] as [null, Error], sigs)
}
Promise.prototype.captureTo = function(sigs?: IPromiseSig | IPromiseSig[]){
return this.then((d: PromiseType<typeof this>) => [d, null] as [PromiseType<typeof this>, null])
.capture((e: Error) => [null, e] as [null, Error], sigs)
}
使用例子
async function API(e?: any){
return Promise.reject(e)
}
async function run(){
// 只对 500 错误做捕获
const [data, error] = await API(500).captureTo([500])
console.log(data, error)
}
// null 500
需要注意的是,被忽略的错误是直接向外抛出的,一些后续操作(加载状态重置等)需要通过finally()
处理
let loading = false
async function API(e?: any){
return Promise.reject(e)
}
async function run(){
const [data, error] = await API(500).captureTo([500]).finally(() => {
// 始终会执行
loading = false
})
// 错误被忽略时不会执行
loading = false
console.log(data, error)
}
网友评论