文章参考了霍春阳的《Vue.js设计与实现》,是自己在阅读过程中的一些思考和理解
例子:
let finalData
watch(obj, async () => {
// 发送请求
const res = await fetch('/path/to/request')
// 请求赋值
finalData = res
})
一个简单的请求赋值操作。但是,如果在短时间内两次改变obj的值,最后finalData得到的值是第一次还是第二次的呢?考虑到请求有时间延迟,但是我们要的肯定是第二次的结果,就说明第一次的请求是过期的。所以需要一个操作让watch知道第一次的已经是过期的,不能将第一次的结果赋值给finalData。代码如下:
let oldValue, newValue
// 存储用户注册的过期回调函数
let cleanup
// 定义onInvalidate函数
function onInvalidate(fn) {
// 赋给cleanup
cleanup = fn
}
// 将scheduler封装出去
const job = () => {
newValue = effectfn()
// 在调用回调函数之前,先判断有无过期过期回调函数,有则立即执行
if (cleanup) {
cleanup()
}
// 将过期回调传给用户以调用和传入
cb(oldValue, newValue, onInvalidate)
oldValue = newValue
}
// 用户使用
let finalData
watch(obj, async (newVal, oldVal, onInvalidate) => {
// 设置当前改变的标志,代表当前的副作用函数是否过期,false代表未过期
let expired = false
// 调用onInvalidate,传入当前的一个过期回调
onInvalidate(() => {
// 如果过期则将标志位设为true,代表已过期
expired = true
})
const res = await fetch('/first.html')
// 如果过期了则不再将当前值赋予finalData
if(!expired) {
finalData = res
}
})
分析:如果这里两次短时间内修改obj的值。第一次改变时会正常调用watch的回调,传入过期的副作用函数,cleanup设置为第一次的过期回调。第二次改变时,此时检测到cleanup有值,执行第一次的过期回调,则在第一次请求中,不会将res赋予finalData。执行第二次的回调时,cleanup变更为第二次的过期回调,等待是否存在的第三次改变以将第二次设为过期。这样就能避免过期的副作用函数带来的影响。