事件循环是js的一种执行机制;
提到事件循环首先要讲知道js的执行机制是怎么样的,才能清楚怎么算一个事件循环;
js的执行机制:
js是一门单线程语言,它的执行顺序是根据出现的顺序依次执行的。但是这样就会有出现一个问题,比如想打开一个有图文的页面,如果页面里的图片很大,图片一直没有加载出来,后面的文字也不能显示,只能等着图片加载出来之后再去加载,这显然不合理啊,不合理就会推动改变。为了解决这个问题,就有了同步任务/异步任务这个概念。
需要注意的是,这里的同步任务和异步任务的定义并不是真正意义上的‘多线程’,而是通过单线程模拟出来的。
同步/异步的执行逻辑如下图所示:
在我理解,同步任务/异步异步,其实就是同步的就直接放到主线程里去执行,异步的就是放到异步这个队列里,等待主线程执行完之后去执行。一个最简单的例子,比如:
setTimeout(function(){
console.log(1);
},2000)
console.log(2);
按照js执行机制,应该先执行setTimeout,但是setTimeout是一个异步任务,进入执行栈之后先被放到异步队列Event Table中并注册回调函数,2s后将它的回调函数移入Event Queue中,等待同步任务中的任务执行完之后再来执行; 继续往下走遇到了console,它是个同步任务,直接放进主线程中执行; 执行完console后,当前主线程没有其他任务,开始执行setTimeout的注册函数; 因此结果为2,1;
这里又要引出两个新的概念:宏任务和微任务:
宏任务:包括整体代码script,setTimeout,setInterval;
微任务:Promise,process.nextTick;
一个宏任务执行完之后,会去执行当前排在队列中的微任务,等微任务执行完之后,会再去执行新的宏任务,循环往复直到全部任务都执行完;
刚刚举得是一个最简单的例子,它就是一个事件循环,但是实际场景往往复杂很多,比如:
console.log(1);
setTimeout(function(){
console.log(2);
})
new Promise(function(resolve){
console.log(3);
resolve();
}).then(function() {
console.log(4);
})
setTimeout(function(){
console.log(5);
new Promise(function(resolve){
console.log(6);
resolve();
}).then(function() {
console.log(7);
})
})
console.log(8);
这段的执行逻辑是:
- 从一个整段代码script开始作为一个‘宏任务1’进入主线程,遇到console.log(1),输出1
- setTimeout的回调函数是一个宏任务,暂时存放到Event Queue中,记作‘宏任务2’;
- new Promise直接执行,输出3,then被分发到微任务Event Queue中,记作‘微任务1’;
- setTimeout的回调函数是一个宏任务,分发到Event Queue中,记作‘宏任务3’;
- console.log(8),输出8,到这里,‘宏任务1’执行完了,开始执行微任务;
- 发现了一个微任务‘微任务1’,输出4;
- 此时第一轮宏任务‘宏任务1’执行完了,第一轮的微任务‘微任务1’也执行完了,到这里就结束了一个事件循环,开始第二个事件循环;
- 开始一个新的宏任务,发现有个‘宏任务2’,输出2;
- 此时第二轮宏任务‘宏任务2’执行完了,没有微任务,开启新一轮的宏任务;
- 开启新一轮的宏任务‘宏任务3,遇到了console,输出5;
- new Promise直接执行,输出6,then被分发到微任务Event Queue中,记作‘宏3-微任务1’;
- 开始执行微任务‘宏3-微任务1’,输出7;
至此,全部任务都执行完了,输出结果为:1,3,8,4,2,5,6,7
讲到这里,对于事件循环一个浅显的介绍就讲完了,还有很多细节的点没有提到,有兴趣可以再去了解。
可以说,在面试中,面试官问事件循环,他最想听的其实是你对于js执行机制的一个理解,因为解释事件循环就得解释js的执行机制,了解了js的执行机制,知道js的代码是如何执行的,才能知道什么算一个事件循环。