20行代码,借助scheduler实现递归轮询类

业务场景复现

彦祖们, 假设你开了一家 4s 店, 店名叫做 Tesla·Qin

你想定时查看店内所有汽车的状态,但是这个接口又比较耗时

笔者最近就遇到了这样一个业务场景,由于接口查询比较耗时,所以遇到了很多异步导致的问题

在线 Demo

老规矩,先上体验 Demo

codepen.io/firstblood9...

模拟业务代码结构

js 复制代码
// 模拟后端接口
const getStauts = ()=>{
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve([1,2,3])
    },10000 * Math.random())
  })
}
setInterval(()={
    this.getStatus()
},5000)

也就是 我们每隔 5000ms 会去定时请求这个接口

这马上遇到了一个问题,假设我们接口的响应时间一直是 100000ms

那么我们就会在前10s内发出两个请求

当然更为致命的问题是由于异步响应,导致我们在视图上看到的可能是上一次接口返回的数据

优化代码逻辑

此刻我们可以设置一个 setTimeout 来等待接口完成时再做定时请求

js 复制代码
const run = ()=>{
    this.getStatus().finally(()=>{
        setTimeout(run,5000)
    })
}

此时此刻,已经完全能满足业务需求,但是一点都不通用

彦祖们应该不会满足于此吧?

利用 scheduler 封装通用函数

在开始之前,让我们先来了解下 scheduler 是个什么东西?

  • Scheduler

developer.mozilla.org/en-US/docs/...

  • postTask

developer.mozilla.org/en-US/docs/...

巴拉巴拉一大堆,直接看他咋用就完事了...

大致意思是接收两个参数,第一个是执行函数,第二个是配置参数

  • 配置函数

彦祖们,直接答疑解惑

segmentfault.com/q/101000004...

开始设计函数

其实我们接收到的功能核心 只有两个东西

  1. 执行函数
  2. 执行函数对应的延迟时间

那么我们大致就能写出以下代码

js 复制代码
function run(executor,delay){
    executor().finally(()=>{
        setTimeout(()=>{
            run(executor,delay) // 这里遇到了 this 指向问题
        },delay)
    })
}

到此为止,我们似乎还没用到 scheduler 彦祖们 别急,接着往下看

如何取消对应任务?

某些特殊场景下 我们可能需要取消这个轮询函数

此时 scheduler 以及它的第二个参数 options.signal 该上场了

官方也是巴拉巴拉一大堆,大致意思就是给我传递一个信号指令,我能帮你取消这个任务

此时又引出了另外一个 controller : TaskController

developer.mozilla.org/en-US/docs/...

这玩意继承自 AbortController

完整的通用函数

话不多说 直接上代码

js 复制代码
function run(executor,delay){
    const  abortController = new TaskController()
    scheduler.postTask(()=>{
        executor().finally(()=>{
            setTimeout(run.bind(this),delay) // 修复上面的 this 指向问题
        })
    }, {
      signal: abortController.signal
    })
    return abortController.abort // 返回一个取消函数,这也是一个常用的技巧
}

const cancel = run(getStatus,5000) // 调用 cancel 可以取消任务

彦祖们会发现 line:5 这里遇到了 this指向问题

这是面试八股文遇不到的,程序猿还是要实战啊~

至此 一个完整的通用函数就算封装完成了,代码非常简单

利用 scheduler 封装通用类

其实函数的功能还是有限,比如说用户想在 getStatus 成功后执行 then 逻辑,失败后执行 catch 逻辑

我们希望是这么调用的(catch 是关键字,统一改成 _ 开头)

js 复制代码
const ins = new RecursiveExecutor(getStauts,{
  _then(res){
    console.log('__SY__🍦 ~ then ~ res:', res) // 执行对应的 then 逻辑
  },
  _catch(res){
    console.warn('__SY__🍦 ~ res:', res) // 执行对应的 catch 逻辑
  },
  delay:1000 // 延迟执行时间
})

彦祖们,结构已经出来了 实现就非常简单了,直接上代码

js 复制代码
class RecursiveExecutor{
  constructor(executor,options){
    this.executor = executor
    this.options = options
    this.abortController = new TaskController()
  }
  run(){
    const {_then,_catch,delay} = this.options
    const {executor,abortController,run} = this
    scheduler.postTask(()=>{
      executor().then(_then).catch(_catch).finally(()=>{
        setTimeout(run.bind(this),delay)
      })
    }, {
      signal: abortController.signal
    });
  }
  cancel(){
    this.abortController.abort();
  }
}

当然这只是抛砖引玉,彦祖们 可以自行拓展更多功能~

写在最后

业务项目中,类似的业务场景是非常多见的

但是要考虑抽象成一个公共的底层功能,这是非常考验我们 抽象和编程能力的

说归说, 其实最后还是 cv 最香,要什么自行车么😚

个人能力有限

如有不对,欢迎指正 🌟 如有帮助,建议小心心大拇指三连🌟

相关推荐
牧野星辰20 小时前
eslint你不懂的都在这里,不信你进来看嘛~
前端·eslint
FogLetter20 小时前
设计模式奇幻漂流:从单例孤岛到工厂流水线
前端·设计模式
ohyeah20 小时前
深入理解 JavaScript 数组:从创建到遍历的完整指南
前端·javascript
逛逛GitHub20 小时前
GitHub 开源 AI 好玩神器,自动记录你的一天。
前端·github
hollyhuang20 小时前
正则校验:校验只能输入数字且首位不能是0
前端
一室易安20 小时前
模仿elementUI 中Carousel 走马灯卡片模式 type=“card“ 的自定义轮播组件 图片之间有宽度
前端·javascript·elementui
在下胡三汉20 小时前
创建轻量级 3D 资产 - Three.js 中的 GLTF 案例
开发语言·javascript·3d
脸大是真的好~20 小时前
黑马JAVAWeb -Vue工程化 - Element Plus- 表格-分页条-中文语言包-对话框-Form表单
前端·javascript·vue.js
程序猿_极客20 小时前
【期末网页设计作业】HTML+CSS+JS 香港旅游网站设计与实现 (附源码)
javascript·css·html
一个小潘桃鸭20 小时前
记录:echarts tooltip内容过多时,会导致部分内容遮挡
前端