多个请求间断失败,如何优雅处理?
背景
试想这样一个场景:在访问系统的某个页面时,会发出多个请求,恰巧在某个时间点,登录信息过期了,这个时候需要自动跳转到登录页。由于请求返回的时间不一致,可能会多次执行跳转,造成使用困扰。
问题核心
法律常识告诉我,不能对同一行为进行多次评价。所以,我们也不能对同一错误进行多次处理。
解决方案:发布订阅
每次请求,可以订阅一次事件(譬如叫 logoutHandler),当一个请求出错则广播错误信息,那些订阅了这个事件的请求,就不再处理后续的响应了。伪代码如下:
const PubSub = {}
const request = opt => {
let isErrorHandled = false
PubSub.subscribe('logoutHandler', () => isErrorHandled = true)
return fetch(opt)
.catch(e => {
if (isErrorHandled) throw null
if (e.status === 'logout') {
PubSub.publish('logoutHandler', e)
// redirect to login page
}
throw null
})
}
很好,解决了上面的问题。但,这样做会导致内存随请求的增加而线性增长。有没有更好的方案?
解决方案:时间治愈一切
在初始化过程中,设置一个起始时间戳。每次请求,再对该请求附加一个当前时间戳。当发现用户信息失效,则重置起始时间戳。根据时间的大小,决定是否处理错误信息。伪代码如下:
let __startTime = Date.now()
const request = opt => {
const requestTime = Date.now()
return fetch(opt)
.catch(e => {
const isErrorHandled = requestTime <= __startTime
if (isErrorHandled) throw null
if (e.status === 'logout') {
__startTime = Date.now()
// redirect to login page
}
throw null
})
}
这种方式,无论是时间开销还是内存开销,都可以忽略不计。
进一步优化
当应用已经跳转到登录页了,即使之前的请求响应正常,其实也不需要再处理了。
let __startTime = Date.now()
const request = opt => {
const requestTime = Date.now()
return fetch(opt)
.then(res => { // 新增:不再处理正常响应
const isErrorHandled = requestTime <= __startTime
if (isErrorHandled) throw null
return res
})
.catch(e => {
const isErrorHandled = requestTime <= __startTime
if (isErrorHandled) throw null
if (e.status === 'logout') {
__startTime = Date.now()
// redirect to login page
}
throw null
})
}