浅谈javascript事件环微任务和宏任务队列原理
JS事件环
JS程序的运行是离不开事件环机制的,这个机制保证在发生某些事情的时候我们有机会执行一个我们事先预定好的函数,事情发生的时候JS会将相应的函数入栈执行然后出栈,但是关于事件环我们还有一些未知的东西,例如,setTimeout我们习惯称他为定时器,但是可能很多人没有意识到,这个东西和我们常用的一些事件没什么不同,只不过我们通常所说的事件大多需要用户触发,而setTimeout不用用户自己触发,而是指定时间之后触发;那么问题来了,如果我们将时间设置为0会发生什么?会立即执行么?
setTimeout、DOM或者HTTP请求这部分其实并不在v8引擎中,这些属于webAPI,javascript是一个单线程的语言,也就意味着一次只能做一件事情,这个事实从未改变
执行原理
JS中所有的方法都会被推入栈中执行,执行完成被弹出,在遇到异步代码的时候,例如setTimeoutMutationObserverPromise异步的部分会由其他掌管webAPI的地方执行,等异步有结果之后,回调函数会进入相应的队列,PromiseMutationObserver回调进入微任务队列,setTimeoutsetIntervalrequestAnimationFrame进入宏任务队列。等待主线程的执行栈空了,微任务队列立刻被推入栈中执行,执行完毕开始执行宏任务队列
一个经典的例子
html
js
//Let'sgetholdofthoseelements varouter=document.querySelector('.outer'); varinner=document.querySelector('.inner'); //Let'slistenforattributechangesonthe //outerelement newMutationObserver(function(){ console.log('mutate'); }).observe(outer,{ attributes:true, }); //Here'saclicklistener… functiononClick(){ console.log('click'); setTimeout(function(){ console.log('timeout'); },0); Promise.resolve().then(function(){ console.log('promise'); }); outer.setAttribute('data-random',Math.random()); } //…whichwe'llattachtobothelements inner.addEventListener('click',onClick); outer.addEventListener('click',onClick);
以上代码在,手动点击inner元素的时候会有如下输出
click
promise
mutate
click
promise
mutate
timeout
timeout
截止2020年8月份chromeedgeoperafirefox的结果是统一的,但是在此之前的版本可能会有不同的输出。
一个奇怪的现象
上述代码我们不使用手动触发点击,而是使用inner.click()触发点击,其结果会有很大的不同
click
click
promise
mutate
promise
timeout
timeout
造成以上巨大差异的原因是,手动点击,不是通过函数进入执行栈的方式触发点击事件的回调,所以inner的回调执行完了主线程中的执行栈就是空的可以直接执行队列中任务,然后事件冒泡导致的回调函数才被推入栈运行;而click方法的点击则是通过将click推入栈中执行来达到的,inner的点击回调执行完了之后click方法并没有被弹出栈,而是直接执行冒泡的下一个回调,由于下一个回调有一个重复的属性设置这是不会重复触发MutationObserver的所以mutate的输出只会有一个。等所有的冒泡回调被执行完毕click函数才会被弹出栈。
最后注意,浏览器会尽量预先执行较为敏感的操作。
以上就是浅谈javascript事件环微任务和宏任务队列原理的详细内容,更多关于JavaScript事件环的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。