项目的网络请求层使用了axios,axios是一个及其好用的库,但是在业务使用上出现了一些问题

出现原因

众所周知,前端一般通过ajax向后端请求数据然后处理后显示在网页/app上,当然人都是懒得,总是希望有一些自动完成的事,比如这一次,如果不是需要特殊处理的特定错误,直接弹toast结束。

错误处理还是很容易,axios增加拦截器,如果是指定的错误,直接throw new Error('错误') 剩下的交给promise.catch干就好了,问题集中在如何兜底这个catch,如果不处理,会变成unhandledrejection错误

解决方案

最原始的方案

高阶函数 throwMessage,自动在promise之后增加catch函数,问题是这个要记得手动引入,和自动这个理念还是有点区别的.


  

export function throwMessage<T = any>(responsePromise: Promise<T>) {

return responsePromise.catch((e: Error) => {

console.error(`error`, e)

message.error(e.message)

})

}

每次使用都要throwMessage(promise)容易忘,如果有必要,还要写lint规则防止忘记

webpack方案

既然打包的是webpack,那么使用webpack在转译原始代码的时候注入就好了嘛. 推荐一个promise-catch-loader自动处理好这一系列问题 具体因为没有太研究,不做展开

事件监听方案

上文提到了,如果promise没有捕获错误,会自动变成unhandlered rejection错误,这个是可以全局监听的,具体在MDN上可以看

那么 我们在window上直接监听就好了


window.addEventListener('unhandledrejection', event => {

event.preventDefault()

});

问题是,太全局了

处理Promise链方案

这种是最终处理的方案,当然也是最魔改的方案,考虑到只运行在前端,内存溢出情况不多,方能实现

axios返回的是promise,promise是遵循PromiseA+方案,内部其实是有一条promise链的,我们的目的就在于,在promise链条的最后一个promise上挂上catch,反正预先被处理了最后一个也不会触发

问题来了

如果你直接看promise的话,是只能看到,then,catch,finally这些方法的,并没办法看到promise链,也没办法顺着链访问到最后

如何解决

我们的目标是将其变为链式结构,简单来说,只要每个then和catch都能记下他的后继即可。

所以最终我选择注入promise的两个方法

实现代码

  

/**

* 继承然后注入两个方法

*/

export class ChainedPromise<T> extends Promise<T> {

nextPromise?: Promise<any>

  

then<TResult1 = T, TResult2 = never>(

onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,

onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,

): Promise<TResult1 | TResult2> {

const promise = super.then(onfulfilled, onrejected)

this.nextPromise = promise

return promise

}

catch<TResult = never>(

onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,

): Promise<T | TResult> {

const promise = super.catch(onrejected)

this.nextPromise = promise

return promise

}

}

  

/**

* 暴露给外面的方法,axios通过拦截器,通过封装service都能调用它来在入口处自动注入

*/

function autoTravel<T>(promise: Promise<T>): Promise<T> {

const getLastPromise = function(promise: ChainedPromise<any>): Promise<any> {

if (promise.nextPromise) {

return getLastPromise(promise.nextPromise)

}

return promise

}

  

/**

* Promise内部的自动形成链条,就能顺着链条找到最后一个了

*/

const autoTravelPromise = new ChainedPromise<T>((resolve, reject) => {

promise.then(resolve).catch(responseError => {

// throwMessage为之前定义的,promise上自动加catch的方法

throwMessage(getLastPromise(autoTravelPromise))

reject(responseError)

})

})

return autoTravelPromise

}