# 说在前面
JavaScript 为单线程,意为需要根据任务队列一个任务一个任务的执行下去,任务分为同步任务与异步任务,这两个任务放在不同的地方执行。同步任务放在 主线程 中排队执行,而异步任务则放在 任务队列 中,在需要执行的时刻才会加入主线程中排队执行。异步任务分为 宏任务 与 微任务 ,而微任务的执行要先于宏任务,同步方法执行先于异步任务。
宏任务:(macro) task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。
浏览器为了能够使得 JS 内部 (macro) task 与 DOM 任务能够有序的执行,会在一个 (macro) task 执行结束后,在下一个 (macro) task 执行开始前,对页面进行重新渲染,流程如下:(macro) task-> 渲染 ->(macro) task->...
微任务:microtask, 可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前 task 后,下一个 task 之前,在渲染之前。
所以它的响应速度相比 setTimeout(setTimeout 是 task)会更快,因为无需等渲染。也就是说,在某一个 macrotask 执行完后,就会将在它执行期间产生的所有 microtask 都执行完毕(在渲染前)。
# 参考资源
https://zhuanlan.zhihu.com/p/78113300
# 宏任务与微任务分类
# 宏任务
- script (整体代码)
- setTimeout
- setInterval
- I/O
- UI 交互事件
- postMessage
- MessageChannel
- setImmediate (Node.js 环境)
# 微任务
- Promise.then
- Object.observe
- MutationObserver
- process.nextTick (Node.js 环境)
# 异步任务调度流程
- 执行一个宏任务
- 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 宏任务执行完毕后,将微任务队列中的所有微任务依次执行
- 当前宏任务执行完毕,开始下一个宏任务
# 代码示例 A
1 | // 宏任务 |
上述代码的执行结果为: 2431
# 流程描述
settimeout为异步方法,是宏任务,放在任务队列中Promise的构造函数,为同步方法,放在主线程中Promise.then为异步方法,是微任务,放在微任务队列中console.log为同步方法,放在主线程中- 首先先处理所有同步函数,输出
24 - 开始处理异步函数,微任务处理优先于宏任务,所以依次执行微任务得到结果
3 - 处理宏任务,得到结果
1
# 代码示例 B
1 | console.log(1) |
上述代码的执行结果为: 156234789
# 流程描述
console.log(1)为同步方法,放在主线程中setTimeout为异步方法,宏任务,放在任务队列中Promise构造函数为同步方法,放在主线程中Promise.then为异步方法,微任务,放在微任务队列中setTimeout为异步方法,宏任务,放在任务队列中- 依次执行主线程中的任务,首先是同步方法,得到结果
15 - 执行微任务队列中的任务,得到结果
6 - 执行任务队列中的任务,第一个为
setTimeout,console.log(2)为同步方法,放在主线程中 Promise构造函数为同步方法,放在主线程中Promise.then为异步方法,微任务,放在微任务队列中- 依次执行主线程中的任务,同步方法,得到结果
23 - 执行微任务队列中的任务,得到结果
4 - 执行第二个任务队列中的
setTimeout,流程同上,得到结果789
# 代码示例 C
1 | setTimeout(function () { |
# 结果
promise1 2 then1 queueMicrotask1 then3 setTimeout1 then2 then4 setTimeout2
