nextTick 运行机制

1、JS运行机制

JS执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:
1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
2) 主线程之外,还存在一个“任务队列”(task queue)。只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。
3)一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
4)主线程不断重复上面的第三步。

2、nextTick的作用

nextTick就是设置一个回调,用于异步执行。
就是把你设置的回调放在setTimeout中执行,这样就算异步了,等待当时同步代码执行完毕再执行。

this.$nextTick( _ => {
    this.$refs.saveTagInput.$refs.input.focus();
})

当页面上的元素被重新渲染之后,才会执行指定回调函数中的代码。

3、如何将多个设置的回调在一起执行异步调用

每设置一个nextTick就新建一个setTimeout,毕竟一个setTimeout是异步,两个setTimeout也是异步,两个都要等再同步代码执行完毕之后才执行,那我直接只设置一个setTimeout不就好了。

存在回调函数里,每次调用nextTick,便往数组里面push设置的回调
只注册一个setTimeout,时间为0,用于遍历回调数组,然后逐个执行子项
同步代码执行完毕,setTimeout自然会执行

4、Vue实现nextTick的原理

Vue不止使用setTimeout实现nextTick;
会判断promise是否存在,选择任务类型。如果promise存在,就使用微任务。

5、Vue在一个tick中多次更新数据页面只会更新一次

即使在Vue中多么频繁地修改数据,最后Vue页面只会更新一次。

例如:
数据name被页面引用,name会收集到页面的watcher;
name 被修改时,会通知所有收集到的watcher进行更新(watcher.update);
如果name一时间被修改三次时,按道理应该会通知三次watcher更新,那么页面会更新三次,但是最后只会更新一次。
这是因为:
当数据变化后,把watcher.update函数存放进nextTick的回调数组中,并且会做过滤。
通过watcher.id来判断回调数组,便会执行了更新。

所以,当三次修改数据的时候,会push回调数组,三个watcher.update,但是只有第一次是push成功的,其他的会被过滤掉,因为已经存在了。
所以,不管你修改多少次数据,nextTick的回调数组中只存在唯一一个watcher.update,从而页面只会更新一次。

nextTick的实现原理

nextTick的作用:在下次DOM更新循环结束之后执行的延迟回调

在浏览器环境中,常见的

宏任务(macro task)有setTimeout、MessageChannel、postMessage、setImmediate;
微任务(micro task)有MutationObserver和Promise.then

宏任务耗费的时间是大于微任务的,所以在浏览器支持的情况下,优先使用微任务。

如果浏览器不支持微任务:

首先,nextTick并不是浏览器本身提供的一个异步API,而是Vue中,用过由浏览器本身提供的原生异步API封装而成的一个异步封装方法。

它对于浏览器异步API的选用规则如下,Promise存在取Promise.then, 不存在Promise则取MutationObserver,
MutationObserver不存在setImmediate,setImmediate不存在最后取setTimeout来实现。

nextTick即有可能是微任务,也有可能是宏任务,从优先去Promise和MutationObserver可以看出nextTick优先微任务,
其次是setImmediate和setTimeout宏任务。

同步代码执行完毕之后,优先执行微任务,其次才会执行宏任务

setTimeout(()=>{
  console.log("settimeout 1")
}, 0);

console.log("同步 1");
Promise.resolve().then(()=> {
  console.log("promise 1");
});
this.$nextTick(() => {
 console.log("nextTick")
})
Promise.resolve().then(()=>{
  console.log("promise 1")
})
console.log("同步 1");

setTimeout(()=>{
  console.log("settimeout 1");
}, 0)

实现一个nextTick

myNextTick(callback: Function){
    return Promise.resolve().then(()=>{
      callback();
    });
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值