携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
我之前有说过,其实在一些异步进程中,往往不建议使用watch,比如搜索框,每次输入都会请求接口,如果你用watch去做这个,你会发现它根本无法处理。
sequenceDiagram
watch->>client:第一次修改数据,触发watch
client->>server: 请求A
watch->>client:第二次修改数据,触发watch
client->>server: 请求B
server->>client: 请求B返回
client->>watch:请求B返回值改变obj
server->>client:请求A返回
client->>watch:请求A返回值改变obj
当我们的调度器中如果存在异步函数,我们很难确定它的返回时机,很有可能某个请求后发而先至.这个是第一次的执行还有必要吗?明显第二次的返回值是最新的.
再次总结一下,请求A是watch第一次变化的时候运行的副作用函数,请求B是watch第二次变化的时候运行的副作用函数,当请求B发生的时候,其实我们应当认为,A已经过期,B才是当前应当执行的。
这就是本章的主要内容:过期的副作用。
那么Vue中是怎么做的呢?它在watch每次检测到变更,但是在执行副作用函数之前,添加了一个onInvalidate方法来检测副作用是否过期。 我们再增加一个临时变量来存放过期的副作用
const watch = (source,cb,options)=>{
let cleanup = null
const onInvalidate = (fn)=>cleanup=fn
//当我们执行调度器scheduler的时候,先调用过期的回调
const scheduler = ()=>{
newVal = effectFn()
if(cleanup){
cleanup()
}
cb(newVal,oldVal,onInvalidate)
oldVal = newVal //这里就是昨天说的坑了,如果不重新复制,你的旧值就会一直是错的
}
}
这其实叫竞态问题,其实就是无法保证异步操作的完成会按照他们开始时同样的顺序。而在vuejs中同过优先执行过期回调,但是数据最终会被新执行的副作用覆盖掉,从而解决了这个问题。onInvalidate是一个函数,通过它来注册过期回调,这个函数其实用户可以通过回调函数来自己传入的,这样你就可以自定义某个状态值来判断是不是有最新的数据了,从而决定是否执行某些操作。