评论

一个RequestTask.abort()引发的悲剧

记录requestTask.abort()的使用

背景介绍

我司有一款健康记录的微信小程序产品,为了程序的健壮性,前辈开发者们在产品开发初期就引入了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) {},
    })
  }

鸣谢

最后一次编辑于  10-28  
点赞 2
收藏
评论