每一个代理都是由 事件循环驱动的,事件循环负责收集用事件(包括用户事件以及其余非用户事件等)、对任务进行排队以便在合适的时候执行回调。而后它执行全部处于等待中的 JavaScript 任务(宏任务),而后是微任务,而后在开始下一次循环以前执行一些必要的渲染和绘制操做。
这里的任务队列指的是事件循环开始迭代时的任务队列html
在事件循环开始迭代以后加入到队列中的任务须要在下一次迭代开始以后才会被执行。
Macrotasks
一般指的是Tasks
(下文有介绍),Macrotasks
在标准上是查不到任何相关描述的,这个概念从何而来不得而知,可是业界广泛认可这个概念以跟Microtasks作区分。web
而Microtasks
是写入HTML Standard的标准,而且在浏览器环境有相关api queueMicrotask。api
实际上浏览器中只有任务(Tasks)和微任务(Microtasks)之分,下面来说讲他们的概念和区别。promise
一个 任务 就是由执行诸如从头执行一段程序、执行一个事件回调或一个 interval/timeout 被触发之类的标准机制而被调度的任意 JavaScript 代码。这些都在 任务队列(task queue)上被调度。
在如下时机,任务会被添加到任务队列:浏览器
- 一段新程序或子程序被直接执行时(好比从一个控制台,或在一个
<script>
元素中运行代码)。- 触发了一个事件,将其回调函数添加到任务队列时。
- 执行到一个由
setTimeout()
或setInterval()
建立的 timeout 或 interval,以至相应的回调函数被添加到任务队列时。
当调用setTimeout(fn, time)
和setInterval(fn, time)
添加任务时,第二个参数time
指的是任务被添加到任务队列的时间,而不是fn
被执行的时间,fn
被执行的时间应该是被添加的任务执行的时间。app
一个 微任务(microtask)就是一个简短的函数,当建立该函数的函数执行以后, 而且只有当 Javascript 调用栈为空,而控制权还没有返还给被 user agent 用来驱动脚本执行环境的事件循环以前,该微任务才会被执行。 事件循环既多是浏览器的主事件循环也多是被一个 web worker 所驱动的事件循环。这使得给定的函数在没有其余脚本执行干扰的状况下运行,也保证了微任务能在用户代理有机会对该微任务带来的行为作出反应以前运行。
JavaScript 中的 promises 和 Mutation Observer API 都使用微任务队列去运行它们的回调函数,但当可以推迟工做直到当前事件循环过程完结时,也是能够执行微任务的时机。为了容许第三方库、框架、polyfills 能使用微任务,Window
暴露了queueMicrotask()
方法,而Worker
接口则经过WindowOrWorkerGlobalScope
mixin 提供了同名的 queueMicrotask() 方法。
当你想在一次任务结束以前执行一些代码时(而不是下一次任务再执行),你可使用微任务,具体的使用场景能够看看什么时候使用微任务。框架
- 当执行来自任务队列中的任务时,在每一次新的事件循环开始迭代的时候运行时都会执行队列中的每一个任务。在每次迭代开始以后加入到队列中的任务须要在下一次迭代开始以后才会被执行。
- 每次当一个任务执行上下文为空的时候,微任务队列中的每个微任务会依次被执行。不一样的是它会等到微任务队列为空才会中止执行——即便中途有微任务加入。换句话说,微任务能够添加新的微任务到队列中,并在当前任务结束以前且下一个任务开始执行以前执行完全部的微任务。
在写这篇文章的时候我有一个疑问,既然在一个任务执行上下文为空的时候,再执行微任务队列中的微任务。那微任务是在当前被执行任务离开任务队列以前执行仍是以后执行?这就关系到一个任务的执行时间应不该该包括微任务的执行时间,以及事件循环的执行顺序。webapp
我查了一些资料,在HTML Standard和ECMA标准里好像并无明肯定义任务和微任务队列应该是包含关系仍是相互独立的关系,因此任务和微任务之间的关系应该是浏览器厂商本身去实现的。ide
我这里只测试了Chrome浏览器(版本号是87.0.4280.66)函数
能够看到Chrome是把Run Microtasks
的执行时间算在了Task
的执行时间里的,因此在Chrome环境里任务和微任务是包含关系,也就是说当一个微任务执行时,一定有一个任务在同时执行。