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 最香,要什么自行车么😚

个人能力有限

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

相关推荐
zhougl9961 小时前
html处理Base文件流
linux·前端·html
花花鱼1 小时前
node-modules-inspector 可视化node_modules
前端·javascript·vue.js
HBR666_1 小时前
marked库(高效将 Markdown 转换为 HTML 的利器)
前端·markdown
careybobo3 小时前
海康摄像头通过Web插件进行预览播放和控制
前端
TDengine (老段)3 小时前
TDengine 中的关联查询
大数据·javascript·网络·物联网·时序数据库·tdengine·iotdb
杉之4 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
喝拿铁写前端4 小时前
字段聚类,到底有什么用?——从系统混乱到结构认知的第一步
前端
再学一点就睡4 小时前
大文件上传之切片上传以及开发全流程之前端篇
前端·javascript
木木黄木木6 小时前
html5炫酷图片悬停效果实现详解
前端·html·html5
请来次降维打击!!!6 小时前
优选算法系列(5.位运算)
java·前端·c++·算法