什么是宏任务
- 我们可以将每次执行栈执行的代码当做是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)
- 每一个宏任务会从头到尾执行完毕,不会执行其他。
我们知道 JS引擎线程和 GUI渲染线程是互斥的关系,浏览器为了能够使 宏任务和 DOM任务有序的进行,会在一个 宏任务执行结果后,在下一个 宏任务执行前, GUI渲染线程开始工作,对页面进行渲染。
1 | // 宏任务-->渲染-->宏任务-->宏任务-->渲染.. |
主代码块,
setTimeout
,setInterval
等,都属于宏任务
第一个例子:
我们可以将这段代码放到浏览器的控制台执行以下,看一下效果:
1 | document.body.style = 'background:black'; |
我们会看到的结果是,页面背景会在瞬间变成白色,以上代码属于同一次 宏任务,所以全部执行完才触发 页面渲染,渲染时 GUI线程会将所有UI改动优化合并,所以视觉效果上,只会看到页面变成灰色
第二个例子:
1 | document.body.style = 'background:blue'; |
我会看到,页面先显示成蓝色背景,然后瞬间变成了黑色背景,这是因为以上代码属于两次 宏任务,第一次 宏任务执行的代码是将背景变成蓝色,然后触发渲染,将页面变成蓝色,再触发第二次宏任务将背景变成黑色
什么是微任务
- 我们已经知道 宏任务结束后,会执行渲染,然后执行下一个 宏任务,
- 而微任务可以理解成在当前 宏任务执行后立即执行的任务。
- 也就是说,当 宏任务执行完,会在渲染前,将执行期间所产生的所有 微任务都执行完。
Promise
,process.nextTick
等,属于 微任务。
第一个例子:
1 | document.body.style = 'background:blue'; |
执行后看效果:
- 控制台输出 1 3 2 , 是因为 promise 对象的 then 方法的回调函数是异步执行,所以 2 最后输出
- 页面的背景色直接变成黑色,没有经过蓝色的阶段,是因为,我们在宏任务中将背景设置为蓝色,但在进行渲染前执行了微任务,在微任务中将背景变成了黑色,然后才执行的渲染
第二个例子:
1 | setTimeout(() => { |
执行结果如下:
上面代码共包含两个
setTimeout
,也就是说除主代码块外,共有两个 宏任务, 其中第一个 宏任务执行中,输出 1 ,并且创建了 微任务队列,所以在下一个 宏任务队列执行前,先执行 微任务,在 微任务执行中,输出 3 ,微任务执行后,执行下一次 宏任务,执行中输出 2
总结
- 执行一个 宏任务(栈中没有就从 事件队列中获取)
- 执行过程中如果遇到 微任务,就将它添加到 微任务的任务队列中
- 宏任务执行完毕后,立即执行当前 微任务队列中的所有 微任务(依次执行)
- 当前 宏任务执行完毕,开始检查渲染,然后 GUI线程接管渲染
- 渲染完毕后, JS线程继续接管,开始下一个 宏任务(从事件队列中获取)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Jungle!