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

背景

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

参考

相关推荐
涔溪36 分钟前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞1 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-1 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与1 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun1 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇1 小时前
ES6进阶知识一
前端·ecmascript·es6
小泽呀x2 小时前
微信小程序中使用离线版阿里云矢量图标
微信小程序·小程序
渗透测试老鸟-九青2 小时前
通过投毒Bingbot索引挖掘必应中的存储型XSS
服务器·前端·javascript·安全·web安全·缓存·xss
龙猫蓝图2 小时前
vue el-date-picker 日期选择器禁用失效问题
前端·javascript·vue.js
fakaifa2 小时前
CRMEB Pro版v3.1源码全开源+PC端+Uniapp前端+搭建教程
前端·小程序·uni-app·php·源码下载