微信小程序中接口请求取消实践

背景

众所周知, 微信小程序架构系统分为逻辑层视图层 , 所有的js逻辑是在一个单线程中运行的。 当用户切换到A页面时, A页面接口请求执行, 如果用户快速的切换到B页面,B页面请求会立即执行, 此时如果A页面的请求恰好超时了, 请求超时的toast会在B页面被抛出,会给用户造成困扰, 同时也会影响开发者调查的方向 。所以, 我们希望开发一套设计, 能够满足在跳转页面的时候, 将未完成的同时允许中断的请求中断掉,以达到优化性能同时提升用户体验的目的。

设计思路

  • 小程序的页面不再直接通过Page方法来定义,而是通过统一的customPage来控制, 以达到在页面生命周期中加入统一的逻辑 , 在这里, 通过在onLoad生命周期中加入判断,来取消前一个页面的未完成的请求
  • 定义一个requestAbortTask类, 用于维护可中断请求的信息, 里面包含加入中断请求的方法, 清除实例信息的方法, 中断请求的方法
  • 定义一个通用的request 方法, 所有的请求均通过request 来调用, 该方法允许配置请求是否支持中断, 对于配置了允许中断的请求, 在调用的时候将该请求abort加入到requestAbortTask实例中

具体代码

代码里面有相应注释, 这里不做多余解释

定义customPage.js

js 复制代码
export default function page(config) {
  const APP = getApp()
  Page({
    ...config,
    onLoad(options = {}) {
      config.onLoad && config.onLoad.call(this, options)
      if (APP.globalData && APP.globalData.requestTasks) { // 这里的globalData 需要提前定义, 也可以放到其他地方中
        const allPages = getCurrentPages()
        const currentPage = allPages[allPages.length - 1].route
        APP.globalData.requestTasks.abortOthersByKey(currentPage)
      }
    },
  })
}

定义requestAbortTask.js

js 复制代码
export class RequestAbortTask {
  constructor() {
    this.tasksMap = new Map()
  }

  getTasksByKey(key) {
    return this.tasksMap.get(key)
  }

  addTaskByKey(key, task) {
    if (!this.tasksMap.has(key)) {
      const value = new Set()
      value.add(task)
      this.tasksMap.set(key, value)
      return
    }
    const values = this.tasksMap.get(key)
    values.add(task)
  }

  add(key, task) {
    this.addTaskByKey(key, task)
  }

  removeTaskByKey(key, task) {
    const tasks = this.tasksMap.get(key)
    if (tasks) {
      tasks.delete(task)
    }
  }

  removeAllByKey(key) {
    this.tasksMap.delete(key)
  }

  clear() {
    this.tasksMap.clear()
  }

// 通过页面key, 终止掉所有其他页面的未完成请求
  abortOthersByKey(key) {
    if (this.tasksMap.size) {
      this.tasksMap.forEach((values, taskKey) => {
        if (taskKey !== key) {
          if (values.size) {
            values.forEach((task) => {
              if (task && task.abort) {
                task.abort()
              }
            })
            this.removeAllByKey(taskKey)
          }
        }
      })
    }
  }
}

定义request 方法

js 复制代码
import { RequestAbortTask } from "./requestAbortTask"

export default function request(params, options = {}) {
  const { canAbort } = options
  const APP = getApp()
  const allPages = getCurrentPages()
  const currentPage = allPages[allPages.length - 1].route
  return new Promise((resolve, reject) => {
    const removeAbortTask = (requestTask) => { // 如果请求在切换页面前已经完成了, 在requestTasks中移除
      if (canAbort) { // 这里只对配置了canAbort 的请求做特殊逻辑
        if (APP.globalData.requestTasks) {
          APP.globalData.requestTasks.removeTaskByKey(currentPage, requestTask)
        }
      }
    }
    const requestTask = wx.request({
      ...params,
      success(res) {
        removeAbortTask(requestTask) // 移除该请求中断
        resolve(res)
      },
      fail(err) {
        removeAbortTask(requestTask) // 移除该请求中断
        if (canAbort && err && err.errMsg === 'request:fail abort') {
          // 终止了请求会进入这个逻辑,一般不用特殊处理,直接return掉
          // 接口请求手动终止, 逻辑自定义
        }
        console.log("fail err", err)
        reject(err)
      }
    })
    if (canAbort) { // 将配置了允许中断的请求加入到requestTasks中
      if (!APP.globalData.requestTasks) {
        APP.globalData.requestTasks = new RequestTask()
      }
      APP.globalData.requestTasks.addTaskByKey(currentPage, requestTask)
    }
  })
}

实例:

在页面A中 调用方法

js 复制代码
import page from "../../util/customPage"
import request from "../../util/request"

const app = getApp()

page({
  data: {

  },
  onLoad() {
    request({
      method: "GET",
      url: "https://jsonplaceholder.typicode.com/comments" // 替换实际请求
    }, { canAbort: true })
  },
  handleNavigateTo() {
    wx.navigateTo({
      url: '/pages/secondPage/secondPage', // 当切换页面的时候, 如何没有请求完成的接口,会被终止掉
    })
  }
})

在页面B中

js 复制代码
import page from "../../util/customPage"

page({
  handleBack() {
    wx.navigateBack()
  }
})

代码片段

developers.weixin.qq.com/s/vD548gmj7...

参考

相关推荐
m0_748256781 分钟前
SpringBoot 依赖之Spring Web
前端·spring boot·spring
web1350858863529 分钟前
前端node.js
前端·node.js·vim
m0_5127446431 分钟前
极客大挑战2024-web-wp(详细)
android·前端
若川40 分钟前
Taro 源码揭秘:10. Taro 到底是怎样转换成小程序文件的?
前端·javascript·react.js
潜意识起点1 小时前
精通 CSS 阴影效果:从基础到高级应用
前端·css
奋斗吧程序媛1 小时前
删除VSCode上 origin/分支名,但GitLab上实际上不存在的分支
前端·vscode
IT女孩儿1 小时前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript
m0_748256563 小时前
如何解决前端发送数据到后端为空的问题
前端
请叫我飞哥@3 小时前
HTML5适配手机
前端·html·html5
@解忧杂货铺5 小时前
前端vue如何实现数字框中通过鼠标滚轮上下滚动增减数字
前端·javascript·vue.js