事件循环
众所周知的javascript是一门单线程语言,而事件循环(Event Loop)就是js为了协调事件,交互,渲染,网络等的执行机制。
任务队列
既然是单线程,那么为了防止一个任务耗时过长,导致页面渲染卡死等问题,我们就可以将任务区分为
- 同步任务(synchronous),同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务,
- 异步任务(asynchronous),异步任务指的是,不进入主线程、而进入“任务队列”任务,只有“任务队列”通知主线程。某个异步任务可以执行了,该任务才会进入主线程执行。
我们看图说话:
- 同步和异步任务分别进入不同的执行”场所”,同步的进入主线程,异步的进入Event Table并注册函数。
- 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
- 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
- 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
执行顺序
除了同步任务和异步任务,我们对任务有更精细的定义:
- macrotask主要包含:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(node)
- microtask主要包含:Promise.then、MutaionObserver、process.nextTick(node)
不同类型的任务会进入对应的Event Queue,比如setTimeout
和setInterval
会进入相同的Event Queue。
事件循环的顺序决定了js的执行顺序,执行顺序如下图所示:
我们可以清楚的看到优先执行宏任务,然后查看是否有可执行的微任务,如果有执行微任务,然后开始新的宏任务,依次循环执行。
代码分析
任务源 -> 微任务 -> 宏任务
console.log('start')
setTimeout( function () { // 宏任务
console.log('setTimeout')
}, 0 )
Promise.resolve().then(function() { // 微任务
console.log('promise1');
}).then(function() { // 微任务
console.log('promise2');
});
console.log('end')
// start
// end
// promise1
// promise2
// setTimeout
0. 代码作为宏任务开始执行
1. 执行console.log('start')
2. setTimeout将其回调函数注册到宏任务Event Quene
3. promise执行,then函数分发到微任务Event Quene
4. 执行console.log('end')
5. 有可执行微任务执行console.log('promise1'), console.log('promise2')
6. 开始新的事件循环,发现setTimeout的宏任务回调函数,立即执行