概念
事件循环是在一个javascript引擎等待任务、执行任务、休眠等待更多任务这几个状态之间转换的无限循环。
首先需要先了解下以下几个概念, 单线程、同步、异步
js是单线程,这就意味着所有任务需要排队,一个任务完成之后才能执行另一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
于是js将所有任务分为同步任务和异步任务,
同步:调用立即得到结果的任务,程序从头到尾一个一个执行
异步:先执行一部分等拿到结果(或到时间)再执行后续代码
同步程序执行完成后才执行异步程序。
事件循环运行机制
-
在JavaScript运行的时候,主线程会形成一个栈,通常这个栈被称为调用栈(Call Stack),或者执行栈(Execution Context Stack),所有js代码都会在执行栈中运行。
-
主线程之外还有一个任务队列(task queue)。
执行过程中如果遇到一些异步代码,浏览器会将这些代码放到对应的线程去执行,当异步代码执行完后(如setTimeout时间到了,ajax请求得到响应),该线程就会将执行完的回调函数放到任务队列中等待执行
当主线程执行完栈中的所有代码后会检查任务队列是否有任务要执行,有的话就将任务放到执行栈中执行,如果当前任务队列为空,会一直循环等待任务到来。
任务队列中又分为宏任务和微任务,一次事件循环的执行顺序是:
1.同步任务
2.process.nextTick
3.微任务
4.宏任务
5.setmmediate
也就是说一次事件循环中,会先将微任务放到执行栈中执行,微任务都执行完后再将宏任务放到执行栈中执行
宏任务: 包含执行整体的js代码,事件回调,XHR回调,定时器(setTimeout/setInterval/setImmediate),IO操作,UI render
微任务:Promise.then()、 async/await、process.nextTick(nodejs)、MutationObserver、Object.observe
注意:new Promise执行本身属于同步代码;其中
setImmediate和process.nextTick是nodejs的实现
面试题
var src=["http://www.w3school.com.cn/i/site_photoref.jpg","http://www.w3school.com.cn/i/site_photoexa.jpg","http://www.w3school.com.cn/i/site_photoqe.jpg"]
for(var i=0;i<3;i++){
var img=new Image();
img.src=src[i];
img.onload=function(){
console.log(img)
}
}
输出结果:3个3
分析:js的事件是异步的,js异步最大的特点是得等到主线程跑完才轮到他执行,onload只注册了个事件回调,
主程序for没跑完,它没有机会切入到事件循环执行事件回调,以上代码中遇到异步任务后继续执行for循环,
由于只有一个全局作用域,每次 for 循环修改的都是同一个i,等for循环执行完毕即 i 等于3时,开始执行异步任务
console.log(1);
const promise = new Promise(resolve => {
console.log(2);
resolve();
console.log(3);
});
async function foo() {
console.log(4);
await promise;
console.log(6);
}
foo();
promise.then(() => {
console.log(7);
});
setTimeout(() => console.log(8));
console.log(5);
输出结果:12345678