业务场景复现
彦祖们, 假设你开了一家 4s 店, 店名叫做 Tesla·Qin
你想定时查看店内所有汽车的状态,但是这个接口又比较耗时
笔者最近就遇到了这样一个业务场景,由于接口查询比较耗时,所以遇到了很多异步导致的问题
在线 Demo
老规矩,先上体验 Demo
模拟业务代码结构
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
- postTask
巴拉巴拉一大堆,直接看他咋用就完事了...
大致意思是接收两个参数,第一个是执行函数,第二个是配置参数
- 配置函数
彦祖们,直接答疑解惑
开始设计函数
其实我们接收到的功能核心 只有两个东西
- 执行函数
- 执行函数对应的延迟时间
那么我们大致就能写出以下代码
js
function run(executor,delay){
executor().finally(()=>{
setTimeout(()=>{
run(executor,delay) // 这里遇到了 this 指向问题
},delay)
})
}
到此为止,我们似乎还没用到 scheduler
彦祖们 别急,接着往下看
如何取消对应任务?
某些特殊场景下 我们可能需要取消这个轮询函数
此时 scheduler
以及它的第二个参数 options.signal
该上场了
官方也是巴拉巴拉一大堆,大致意思就是给我传递一个信号指令,我能帮你取消这个任务
此时又引出了另外一个 controller : TaskController
这玩意继承自 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
最香,要什么自行车么😚
个人能力有限
如有不对,欢迎指正 🌟 如有帮助,建议小心心大拇指三连🌟