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

背景

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

参考

相关推荐
__WanG3 分钟前
Flutter将应用打包发布到App Store
前端·flutter·ios
leluckys6 分钟前
flutter 专题十七 Flutter Flar动画实战
前端·flutter
豆包MarsCode22 分钟前
我用豆包MarsCode IDE 做了一个 CSS 权重小组件
开发语言·前端·javascript·css·ide·html
22x艾克斯31 分钟前
Web Notifications API-让网页也能像QQ一样实现消息通知
前端
想你的风吹到了瑞士39 分钟前
变量提升&函数提升
前端·javascript·vue.js
生椰拿铁You1 小时前
12 —— Webpack中向前端注入环境变量
前端
Huazzi.1 小时前
免费好用的静态网页托管平台全面对比介绍
前端·网络·github·web
吃土少女古拉拉1 小时前
前端和后端
前端·学习笔记
尘浮生2 小时前
Java项目实战II基于SpringBoot的共享单车管理系统开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·微信小程序·小程序
寒雒2 小时前
【Python】实战:实现GUI登录界面
开发语言·前端·python