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

背景

众所周知, 微信小程序架构系统分为逻辑层视图层 , 所有的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...

参考

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax