🪵

watch的竟态问题(onCleanup)

 

场景

百度搜索的时候,连续搜索了两次答案,然后没想到第一次请求的数据返回慢了,然而第二次的请求数据先出来了,第一次的请求数据后出来,如果按照平常来说,第一次的请求数据又给覆盖了之前的数据,这样就造成了一些问题
WindrunnerMax
竞态问题通常指的是在多线程的编程中,输入了相同的条件,但是会输出不确定的结果的情况。虽然 Js是单线程语言,但由于引入了异步编程,所以也会存在竞态的问题,而使用 RxJs 通常就可以解决这个问题,其使得编写异步或基于回调的代码更容易。 前边提到了竞态问题通常指的是在多线程的编程中,输入了相同的条件,但是会输出不确定的结果的情况。发生这种情况的主要原因是,当多个线程都对一个共享变量有读取-修改的操作时,在某个线程读取共享变量之后,进行相关操作的时候,别的线程把这个变量给改了,从而导致结果出现了错误。在这里的多个线程中,起码有一个线程有更新操作,如果所有的线程都是读操作,那么就不存在什么竞态条件。总体来说,最低是需要 thread1#load - thread2#update这种的模式,当其中一个线程进行更新共享变量操作的时候,另一个线程不管是读取变量还是更新变量都容易出现错误,要么读取脏数据,要么丢失更新结果,通常会使用加锁或者原子操作的方式来消除竞态的影响。 回到 Js当中,虽然 Js是单线程语言,但由于引入了异步编程,所以也会存在竞态的问题。举一个简单的例子,我们经常会发起网络请求,假如我们此时需要发起网络请求展示数据,输入 A时弹出 B,输入 B时弹出 C,要注意反悔的数据都是需要通过网络发起请求来得到的,假设此时我们快速的输入了 A又快速输入了 B,如果网络完全没有波动的情况下,我们就可以正常按照顺序得到 B、 C的弹窗,但是如果网络波动了呢,假设由于返回 B的数据包正常在路上阻塞了,而 C先返回来了,那么最后得到的执行顺序可能就是 C、 B的弹窗了。在这里只是一个顺序问题,如果我们做搜索的时候,更加希望的是展示输入的最后的值的搜索结果,那么按照上边的例,我们希望得到最后输入的那个字母的下一个字母,也就是顺序输入 AB希望得到 C,但是却也有可能得到 B 。 const fetch = text => { if(!text) return Promise.resolve(""); const response = String.fromCharCode(text[text.length - 1].charCodeAt(0) + 1); return new Promise(resolve => { setTimeout(resolve,
Video preview
 
 
连续触发watch时需要清理之前的watch操作 达到以最后一次返回结果为准的目的
  1. 第一次调用watch回调函数时候 传入一个取消的回调
  1. 第二次调用watch回调函数的时候执行上一次传入的回调
 
const state= reactive({flag: true, name: 'lyp',address:{num: 6}, age: 30}) let i = 2000; // 模拟ajax 实现 第一次比第二次返回的晚 function getData(timer){ return new Promise((resolve,reject)=>{ setTimeout(() => { resolve(timer) }, timer); }) } // 每次数据变化 都会执行watch的回调函数 // 每次都会形成一个私有作用域 传入的onCleanup函数 执行改变的是上一个私有作用域的clear值 // onCleanup 是 vue源码提供给用户的钩子 watch(()=>state.age,async (newValue,oldValue,onCleanup)=>{ let clear = false; // 将 终止的调函数 给到vue源码中的cleanup(也就是传递给下一层) // 终止函数的调用会有vue源码自动执行 onCleanup(()=>{ clear = true; }) i-=1000; let r = await getData(i); // 第一次执行1s后渲染1000, 第二次执行0s后渲染0, 最终应该是0 if(!clear){document.body.innerHTML = r;} },{flush:'sync'}); // {flush:'sync'} 表示同步执行 state.age = 31; state.age = 32;