Event Loop
JS语言的一大特点就是单线程,为了协调事件,用户交互,脚本,UI渲染和网络处理等行为,防止主线程的不阻塞,Event Loop的方案应运而生。
根据规范,事件循环是通过任务队列的机制来进行协调。一个事件循环中,可以有一个或者多个任务队列,一个任务队列是一系列有序任务的集合;每个任务都有一个任务源,源自同一个任务源的任务添加到同一队列中,不同源添加到不同队列。任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。
在事件循环中,每进行一次循环操作称为tick,每一次tick处理的关键步骤如下:
- 执行一个宏任务(执行栈中没有就从Task队列中去取);
- 执行过程中遇到微任务:将它添加到Microtask队列中;
- 执行过程中遇到宏任务:将它添加到Task队列中;
- 执行完毕后(执行栈为空),立即依次执行当前Microtask的所有微任务;
- 开始渲染,GUI线程接管渲染;
- 渲染完成后,JS线程接管,开始下一个宏任务。

宏任务:每次执行栈执行的代码,包括每次从事件队列中获取一个事件回调并放入执行栈中执行。
script(整体代码)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js)
微任务
在当前宏任务执行结束后立刻执行的任务。即渲染之前;下一个task之前。
Promise.then
Object.observe
MutaionObserver
process.nextTick(Node.js 环境)
console.log('script start');
setTimeout(function() {
console.log('timeout1');
}, 10);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => console.log('timeout2'), 10);
}).then(function() {
console.log('then1')
})
console.log('script end');
简要解释一下:
最初始,宏任务队列中,只有一个script任务,首先遇到了console.log,输出 script start; 接着往下走,遇到setTimeOut,将其分发到宏队列;接着遇到 promise,new promise 中的代码立即执行,输出 promise1, 然后执行 resolve ,遇到 setTimeout ,将其分发到宏队列中去,记为 timemout2, 将其 then 分发到微任务队列中去,记为 then1;接着遇到 console.log 代码,直接输出 script end。
接着检查微任务队列,发现有个 then1 微任务,执行,输出then1 再检查微任务队列,发现已经清空。开始检查宏任务队列,执行 timeout1,输出 timeout1; 接着执行 timeout2,输出 timeout2 至此,所有的都队列都已清空,执行完毕。
浙公网安备 33010602011771号