Appearance
vue3 响应式系统原理 - scheduler
scheduler
可调度性是响应系统非常重要的特性。可调度就是指,当 trigger 动作触发副作用函数重新执行时,有能力决定副作用函数执行的时机、次数以及方式
调度器的基本结构
// effect 为注册副作用函数的函数
// effectFn 为副作用函数
const effect = (effectFn, options={}) => {
...
}
// 更新数据
function trigger(target, key, newValue) {
target[key] = newValue
const depsMap = bucket.get(target)
if (!depsMap) return
const effects = depsMap.get(key)
const effectsToRun = new Set()
effects && effects.forEach(effectFn => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn)
}
})
effectsToRun.forEach(effectFn => {
// 如果一个副作用函数存在调度器,就调用该调度器
if (effectFn.options.scheduler) {
effectFn.options.scheduler(effectFn)
} else {
// 否则,就直接执行副作用函数
effectFn()
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
通过在 options 中添加调度器函数,可以控制副作用函数执行的时机
scheduler - 控制执行次数
同样是在 options 的 scheduler 函数中,书中利用 jobQueue 和 flushJob 两个函数实现了控制执行次数
const data = {foo: 1}
const jobQueue = new Set()
// 创建一个 promise 实例,将任务添加到微任务队列中
p = Promise.resolve()
let isFlushing = false // 代表是否正在刷新队列
function flushjob() {
if (isFlushing) return
isFlushing = true
p.then(() => {
jobQueue.forEach(job => job())
}).finally(() => {
isFlushing = false
})
}
effect(() => {
console.log(obj.foo)
}, {
scheduler(fn) {
jobQueue.add(fn)
flushJob()
}
})
obj.foo++
obj.foo++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
原理分析
以上函数之所以可以控制执行次数,原因在于巧妙地利用了微任务
effect
函数执行,将副作用函数也就是effect
中传入的第一个参数推入到jobQueue
队列中。打印出1
- 执行
flushJob
函数。isFlushing
为false
,将JobQueue
中的函数添加到微任务队列中执行。此时,isFlushing
为true
- 执行
obj.foo++
, 触发trigger
函数,再次执行scheduler
中的内容。副作用函数被推入到jobQueue
队列中。由于isFlushing
为 true,代表正在刷新队列。 - 执行第二个
obj.foo++
, 同上一步一样 - 此时,脚本执行完毕,也就是宏任务执行完毕,开始执行微任务队列。执行
JobQueue
中的函数,由于它是一个set
,所以其实只有一个函数(队列中一共存在两个函数)。经过了两次的自增,foo
已经变为了 3,所以此时打印出的就是3