事件循环
事件循环(Event Loop):指的就是同步、异步的任务分别进入不同的执行环境,最后都同步回归主线程,即主执行栈,异步进入到任务队列(Event Queue)。主线程串行执行任务完毕后,会去任务队列(Event Queue)读取相应的任务,继续串行在主线程执行。以上不断重复的过程被成为事件循环。
事件循环中重要的有2点:主线程、任务队列(Event Queue)。
下面是一个典型的事件循环例子:
step1:主线程读取JS代码,此时为同步环境,形成相应的堆和执行栈;
step2: 主线程遇到异步任务,指给对应的异步进程进行处理(WEB API);
step3: 异步进程处理完毕(Ajax返回、DOM事件处罚、Timer到等),将相应的异步任务推入任务队列;
step4: 主线程执行完毕,查询任务队列,如果存在任务,则取出一个任务推入主线程处理(先进先出);
step5: 重复执行step2、3、4;称为事件循环。
实际中应用
1、onclick事件
由浏览器中的DOM binding模块处理,事件触发时,回调函数添加到任务队列中。
2、setTimeout
由浏览器内核的Timer模块处理,时间到达时,回调函数添加到任务队列中。
3、ajax
由浏览器内核的Network模块处理,网络请求返回后,添加到任务队列中。
任务队列
所有的任务可以分为同步任务、异步任务。
同步任务会直接进入主线程中执行;异步任务会通过任务队列的机制(先进先出)来执行。
任务队列类型:宏任务(macrotask queue)、微任务(microtask queue)。
异步执行的情况
回调函数 callback
Promise/async await
Generator 函数
事件监听
发布/订阅
计时器
requestAnimationFrame
MutationObserver
process.nextTick
I/O
宏任务:
所有的同步任务
I/O, 比如文件读写、数据库数据读写等等
window.setTimeout/setInterval
window.setImmediate
window.requestAnimationFrame
微任务:
Promise.then catch finally
Generator 函数
async await 和promise是一样的,属于微任务
MutationObserver
任务执行过程:
执行顺序
1、所有任务都在主进程上执行,异步任务会经历2个阶段 Event Table和Event Queue
2、同步任务在主进程排队执行,异步任务(包括宏任务和微任务)在事件队列排队等待进入主进程执行
3、遇到宏任务推进宏任务队列,遇到微任务推进微任务队列
4、执行宏任务,执行完宏任务,检查有没有当前层的微任务。
5、继续执行下一个宏任务,然后执行对应层次的微任务,直到全部执行完毕。
执行实例
console.log(1)
setTimeout(() => {
console.log(2)
}, 0);
console.log(3);
new Promise((resolve) => {
console.log(4);
resolve();
console.log(5)
}).then(() => {
console.log(6);
});
console.log(7)
1 => 3 => 4 => 5 => 7 => 6 => 2
注:一个事件循环,但是任务队列可以有多个。
创建Promise实例时是同步的,故此1、3、4、5、7是同步执行的。
promise.then()、setTimeout都是异步执行的,都会在异步队列中等待执行。
而promise.then()异步会放到microtask queue(微任务队列)中,microtask queue队列中的内容是在当前脚本执行完毕后立即发生的事。所以当同步Promise创建实例完成后,就执行promise.then(),然后把setTimeout放入执行堆栈中执行,故先执行Promise,再执行setTimeout。