背景介绍
我司有一款健康记录的微信小程序产品,为了程序的健壮性,前辈开发者们在产品开发初期就引入了wx.request
响应超时,自动重发的逻辑。本月产品迎来重大迭代,用户量开始增加。sentry中突然有一个错误日志断断续续出现,日志显示前端收到了接口的response,但是response中没有数据。而后端确认接口是有数据的,多么诡异的问题,唯一有迹可循的是,接口报错时,是接口超时没响应后重发的第二次请求
开始排查
作为公司唯一的实习生,我荣获了周末加班排查这个bug的殊荣,打开我的大宝剑,错了,打开我的macbook,打开IDE,我发现了前辈们在接口重发逻辑中写的一个我不认识的东西
this.requestTask.abort()
马上打开官方文档查看
文档让我心里慌的一比,没办法,只能自己去尝试
举个栗子🌰
为了胜利我把重发的核心逻辑剥离出来放在一个单页面的小程序中,大概就是这样👇
data: {
requestTask: null,
retryRequest: null,
retryCount: 0,
timeout: 50 // 为了测试我把接口超时的时间改成了50ms
},
test: function() {
if(this.retryCount>3) {
clearTimeout(this.retryRequest)
return false
}
this.retryRequest = setTimeout(() => {
this.retryCount++
this.requestTask.abort() // 中止上一个请求
this.test() // 调用自己发起下一次请求
}, 50)
this.requestTask = wx.request({
url: 'api',
success: (res) => {
console.log('in success:', res)
clearTimeout(this.retryRequest)
},
fail: (res) => {
console.log('in fail:', res)
}
})
}
RequestTask.abort()分析
来,请大家猜猜下面红色的error是代码错误了还是中断成功了?
恭喜,这是请求中止成功啦。鼓掌!!!
abort()函数执行成功(请求被中止),会进入fail回调和complete回调,如果errMsg == “request:fail abort”,就表示之前的请求被中止了。至于为什么会有红色的报错(没有任何意义),这是爱的鼓励,不要问为什么。
重现问题
我一遍又一遍的点击着小程序开发者工具的编译按钮,发现在我点击十次之内,一定会出现重发多个请求后,有一个请求得到响应,其他请求被取消,而在success中打印res,发现里面的data不见了,就是下面这样👇
推测一下
abort()函数是已经封装好的函数,是一个异步的函数
原先的逻辑中,abort被执行,但是并不知道请求是否已经彻底终止,就发起了下一个请求
this.requestTask.abort()
this.test()
就在终止请求的时,下一个请求响应,底层开始处理请求的响应,上一次的终止逻辑被停了。最后,上一个来不及终止的请求也得到了响应,各种巧合导致其中一个得到响应的请求,success回调中打印不出来data
改动一下
在定时器setTimeout中执行requestTask.abort(),在fail的回调中,只有判断出res.errMsg === 'request:fail abort’的情况下,表示上一个请求已经彻底终止。重发下一次请求
test: function() {
this.retryRequest = setTimeout(() => {
this.requestTask.abort()
}, 50)
this.requestTask = wx.request({
url: 'api',
success: (res) => {
console.log(res)
clearTimeout(this.retryRequest)
},
fail: (res) => {
if (res.errMsg === 'request:fail abort')
this.test()
},
complete: function (res) {},
})
}