每次我在写技术类文章的时候都喜欢用引用一个神话故事或者一位超级英雄。没错,由于个人中二病很严重,写代码的时候都幻想本身有一对机械手臂帮我在那啪啪啪的调试bug,别想歪了不是那种啪啪啪。 此次我要说的就是 蚁人html
变大 🐜 --- 变大 🐜 --- 变大 🐜--- 变大 🐜 --- 变大 🐜html5
为何这么说那?由于你想在<美队3>里蚁人内战之中变得很大,可是问题就来了,变大以后虽然力量有了加成,可是速度变得很慢,我如今不说你应该猜出宏任务的缺点了吧? 同理,蚁人变小以后灵巧了很多,能够跟不少蚂蚁沟通,这些蚂蚁井井有理帮助蚁人,是否是这些小🐜很像微任务,那么微任务的好处你也猜到了吧?node
咱们来引入谷歌的一位大神 Jake 的文章做为说明,原文标题:《Tasks, microtasks, queues and schedules》原文地址:Tasks, microtasks, queues and scheduleschrome
首先看一段代码:segmentfault
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
复制代码
打印顺序是什么?(ctrl+A查看答案)promise
正确答案是: script start, script end, promise1, promise2, setTimeout浏览器
这是为何那?bash
我稍稍解释下,既然我说了,宏任务是变大那么效率确定低,微任务反之确定是有序进行效率确定要高一些,那么咱们能看出,SetTimeout确定是效率低的。多线程
由于SetTimeout等于在开一条多线程干另外一件事,那么SetTimeout是宏任务无疑的。与之相反的异步处理方式固然是微任务了。dom
也就是说Promise对象返回的状态给了then,then就是那些小蚂蚁,这些小蚂蚁会有条不紊的工做下去,.then().then().then().then()的链式调用,只要你愿意就会无穷无尽。
那么这个问题好解了,因此 'script start'确定会被先打印出来,script end随后打印出来,SetTimeout先日后放由于效率低,而后'promise1'打印,'promise2'打印 两个微任务执行完毕,最后安排完小蚂蚁后蚁人变大 触发SetTimeout最后执行。
由于SetTimeout这种宏任务很容易在触犯的时候关联着DOM元素的变化,SetTimeout不能本身完成须要等待DOM元素修改后的结果,这样每次触发任务以后还要关联DOM元素的变化,效率确定要低不少。promise就不同了,每次执行的微任务都被蚂蚁排着队处理了,每一个蚂蚁都有条不紊的一个接一个进行处理,这些蚂蚁就造成了一个任务队列。也就是说你们执行的任务都是有顺序的,若是在执行任务的过程当中有新的微任务产生就日后排,等待前面的蚂蚁执行完毕,再去执行新的任务。
可是理想是墙上的美女,现实是炕上的媳妇... 总有些东西会无情的打你脸...
理论是这个理论,实践倒是另外一种实践。有的浏览器打印结果就是不同,啪啪啪打的响不响?
有些浏览会会打印出: script start, script end, setTimeout, promise1, promise2。
他们会在setTimeout以后执行promise的回调,就好像这些浏览器会把promise的回调视做一个新的宏任务而不是微任务。
其实无可厚非,由于promises 来自于ECMAScript 的标准而不是HTML标准。 ECMAScript 有个关于jobs的概念和微任务挺相似的,可是否明确具备关联关系却还没有定论(相关讨论)。然而,广泛的观点是promise应该属于微任务。
简单来讲就是各个浏览器厂商大哥相互之间达不成共识,结果对宏任务和微任务的理解各有差别,这点不只浏览器,就连node的打印结果和浏览器之间的版本都不必定想通!
来点官方的说法吧~我是不肯意看啊
若是说把 promise 当作一个新的 task 来执行的话,这将会形成一些性能上的问题,由于 promise 的回调函数可能会被延迟执行,由于在每个 task 执行结束后浏览器可能会进行一些渲染工做。因为做为一个 task 将会和其余任务来源(task source)相互影响,这也会形成一些不肯定性,同时这也将打破一些与其余 API 的交互,这样一来便会形成一系列的问题。
继续看下面的代码 一段容器
<div class="outer">
<div class="inner"></div>
</div>
复制代码
接着看触发的任务
// Let's get hold of those elements var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the
// outer element
//监听element属性变化
new MutationObserver(function() {
console.log('mutate');
}).observe(outer, {
attributes: true
});
// Here's a click listener… function onClick() { console.log('click'); setTimeout(function() { console.log('timeout'); }, 0); Promise.resolve().then(function() { console.log('promise'); }); outer.setAttribute('data-random', Math.random()); } // …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
复制代码
偷看答案前先试一试啊,tips:日志可能出现屡次哦。 结果以下:
哪一个是对的? 分发click event是一个宏任务,Mutation observer和promise都会进入微任务队列,setTimeout回调是一个宏任务,建议原文观看 DEMO
因此chrome是对的,我以前也不知道只要执行栈中没有js代码在执行,微任务会在回调后当即执行,我以前认为它只会在宏任务结束后执行(Although we are mid-task,microtasks are processed after callbacks if the stack is empty).这个规则来自于HTML标准中关于回调调用的部分
If the stack of script settings objects is now empty, perform a microtask checkpoint — HTML: Cleaning up after a callback step 3
浏览器哪里出错了?
Firefox和Safari在click监听器回调之间正确执行了mutation 回调的微任务,但promise打印结果却出如今了错误的位置。 无可厚非的是jobs和微任务的关系太含糊不清,不过我仍认为应该在click监听器回调之间执行。 Edge咱们早就知道会把promise回调放进错误的队列,但他也也没在click监听器回调之间执行微任务队列,而是在全部监听器回调后执行,这打印click以后只打印了一次muteta,所以这是Edge的一个bug。
而后能够总结了,我我的以为结果以chrome的结果做为标准就能够。