——互联网寒冬中的一件棉被。
为什么会出现事件轮询
我们先记住这个问题,再说事件轮询之前,我想先提一下为什么js有事件轮询这一机制。它是干什么的呢? 这还要从js本身的执行机制说起,js本身就是为了网页交互而出现的一种脚本语音,它是单线程的。
为什么js是单线程呢?
因为js可能会操作dom,可能存在一遍删除dom一边修改dom的情况,如果是多线程的话,必须要确保执行的先后顺序,加大了设计难度。所以设计成单线程的。
那么如果,js中的所有代码都是单线程的话,是不是对用户体验有极大的影响,所以js有专门的异步执行机制,通过事件轮询,来提高用户体验。
在分析事件轮询之前先讲一下js在浏览器里面的执行,这为后续js在浏览器的轮询提供了条件。
多线程的浏览器
众所周知,浏览器是多线程的,大概分为,
- 主进程(负责):页面前进,后退,其他进程的调度,发送AJAX请求等等。
- 图形渲染进程:负责3D渲染,
- 第三方插件进程:负责操作第三方插件。
- 渲染进程(最重要就是浏览器内核负责的部分): 此进程用于js执行,页面渲染这些。
渲染进程:
- js执行线程:里面存在执行栈。(同步执行执行栈里面的任务)
- 事件线程:存在事件列表用于处理异步事件。
- 计时器线程:等待setTimeout运行,并将setTimeout的回调事件放入事件列表中。
- 渲染线程:渲染页面。
- 请求线程:负责发送ajax请求,收到结果之后把回调函数放入事件列表。
这里我们要注意的是,js执行线程与渲染线程是互斥的。这还与操作dom节点有关。
js在浏览器中怎么运行呢
js中的代码分为同步任务,异步任务,异步任务又分为分为微任务与宏任务。 宏任务(setTimeout),微任务(promise)。除了微任务,宏任务剩下的都是同步任务。
首先,script中的代码作为同步代码开始执行,过程中遇到异步代码时候放入相应的线程,等待所需条件达成之后(例如setTimeout等待时间,等待ajax请求)放入事件线程中的微任务队列或者宏任务队列。 待script(同步)代码执行完毕之后,将微任务队列里面的代码放入执行栈中然后顺序执行,如果当中遇到了异步任务继续放入其他线程等待所需条件达成之后放入对应的任务队列。将本轮的所有微任务执行完之后。开启下一轮的宏任务。从宏任务队列中调出任务开始执行,中间将本次宏任务中遇到的微任务,放入任务队列中,待本次宏任务执行完后,清空本次微任务。然后继续下一轮宏任务。
上图
举个栗子
写出一下运行结果
console.log('start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
new Promise((resolve) => {
console.log('promise0');
resolve();
})
.then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
async function foo() {
await bar()
console.log('async1')
}
foo()
function bar() {
console.log('async2')
return Promise.resolve().then(() => {
console.log('promise3');
})
}
console.log('end');
这个是笔者自己遇到的面试题,这里大家一块来分析一下哈。
首先根据本文的描述,先执行script的代码(可以当做一个大的宏任务),打印start,往下走 遇到
setTimeout(
() => {
console.log('setTimeout');
}, 0);
0秒过后,将任务的回调函数放进放进宏任务队列中。
继续往下面走
new Promise(
(resolve) => {
console.log('promise0');
resolve();
}
)
执行同步任务(对于promise来说,调用then方法之前都算是同步任务) 打印promise0, 同时更改promise 的状态为fulfill。 将
then(
function() {
console.log('promise1');
}
)
放入微任务队列。继续执行 遇到异步函数foo(),面对异步函数 (await关键字及之前的部分都算是同步代码,await接受的是一个promise对象,它后面的部分可以当做then方法的回调函数放入微任务) 同步执行foo()中的代码打印 async2,同时将
then(() => { console.log('promise3'); })
放入微任务队列 然后同步打印end. 此时script中代码执行完毕,现在来看一下宏任务和微任务队列中的代码
根据上述流程,应该先将微任务队列里面的所有微任务执行完毕,那边打印promise1,同时将后面then中回调函数。放入微任务队列。
then(function() { console.log('promise2'); });
然后继续打印promise3,同时将console.log('async1'),(这里可以被当做) 放入微任务队列中。
此时微任务的情况是
则继续执行微任务知道没有。就依次打印 promise2 asnyc1.至此本轮微任务执行完毕,开始进入下一轮宏任务。
打印setTimeout。
所以最后答案是
怎么样现在对于,事件轮询和,执行机制有理解没有呢?求赞求赞!