axios兜底错误处理

项目的网络请求层使用了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 }

April 29, 2024 · 1 min · 189 words · 水华