vue3 调度执行-控制执行次数
为什么要控制它的执行次数?请思考以下例子
const data = {foo: 1}
const obj = new Proxy(data, {/*...*/})
// 副作用函数
effect(() => {}
console.log(obj.foo)
})
obj.foo++
obj.foo++
没控制前输出如下
1
2
3
// 期望
1
3
是的我们只关心结果并不关心过程 所以2这个输出是多余的。那么我们有了需求就去实现一下,注意我只是大概实现这个思路并不是讲源码,希望这样能让你更好的去理解源码。
// 创建一个任务队列
const jobQueue = new Set()
// 创建一个异步函数
const p = Promise.resolve()
// 一个标志代表当前是否正在刷新队列
let isFlushing = false
function queueJob(job) {
// 存入队列
jobQueue.add(job)
// 执行所有的任务
queueFlush()
}
function queueFlush() {
// 如果当前已有执行的任务就直接return
// 应为我们只需要把副作用函数放入Set队列里即可,并不需要多次执行
if(isFlushing) return
// 改为执行中的状态
isFlushing = true
p.then(() => {
jobQueue.forEach(item => {
item()
})
// 重置isFlushing 状态
}).finally(() => isFlushing = false)
}
// ---- 测试 ----
let i = 0
// effect副作用函数
const effect = function() {
console.log(i)
}
// 改变数据后手动模拟触发了set即调用queueJob函数
i++
// 该任务没有被立即执行而是放入微任务队列,等待当前代码执行完成之后才执行务队列
// 所以 i = (0 + 1 + 10) = 11
queueJob(effect)
// 再次改变 调用queueJob函数
i += 10
// 这时不会被加入队列,因为队列中已经存在该任务了
queueJob(effect)
// 输出
11
整段代码的意思是连续执行两次queueJob,这意味着同一个effect会被add两次但是由于Set数据结构能去重得能力,最终jobQueue 只会有一个effect副作用函数。这时queueFlush也会类似的执行两次,但是由于isFlushing 标志的存在,实际上在一个事件循环内只会执行一次,即在微任务队列里只执行一次。当微任务队列执行时会触发存在里面的副作用函数。这时i已经是11了,所以最终输出了11,并不会输出其他多余内容,符合我们的预期。