每日一题之Event Loop输出顺序
❝大家都知道,我们写的
❞js
代码会运行在单线程的环境中(虽然浏览器非单线程),而单线程很容易出现线程阻塞的问题,因此前端就有了异步的概念,而提到异步就不得不说到Event Loop
,各大互联网公司在面试的时候,经常会问到Event Loop相关的知识,而今天的每日一题,便是一道与此相关的题目,一起来看看吧。
基础知识讲解
在我们日常写代码的时候,如果遇到请求数据,一般会通过axios
或fetch
发送一个异步请求,然后继续执行其他代码,等异步请求回来之后,再去执行异步请求的回调函数,这个过程是如何管理的呢?
在js
中,任务被分为宏任务与微任务这两种,而上面我们说的普通代码就属于宏任务,而异步请求属于微任务。我们先来看一下宏任务和微任务都包含了哪些内容。
❝宏任务:普通代码(script), setTimeout, setInterval, I/O, UI交互事件,postMessage 等等
❞
❝微任务: Promise.then, MutationObserver 等等
❞
在代码执行过程中,宏任务和微任务都采用的是先进先出的队列来调用执行的,如何去理解这句话呢,我们先执行一段代码:
console.log('start')
setTimeout(() => {
console.log('setTimeout')
})
new Promise(resolve => {
console.log('promise')
resolve()
}).then(() => {
console.log('then')
})
console.log('end')
上面代码的输出顺序为 start
,promise
,end
,then
,setTimeout
,为什么是这样的顺序呢?
看一下具体执行步骤:
-
代码执行时候首先遇到 console.log('start')
,因为是同步代码,所以直接输出 -
然后遇到 setTimeout
,通过上文可以知道setTimout
属于宏任务,加入到宏任务队列 -
然后遇到 new Promise
, 上文说道Promise.then
属于微任务,但new Promise
却是同步代码,所以输出promise
,同时将then
加入到微任务队列 -
遇到 console.log('end')
,直接输出 -
这一步很关键,当同步代码执行完成之后,首先会先将微任务队列里面的内容逐一执行,所以输出 then
,同时需要注意,如果微任务里面还有微任务,会同时加入到微任务队列,继续执行 -
等到微任务执行完之后,就从宏任务队列里面取出来第一个继续执行,所以输出 setTimeout
。
上面的步骤就是一个完整的执行过程,然后如果宏任务里面又有新的同步代码与微任务,就从1开始重新执行一遍,这个过程被称为eventloop
,整个过程可以分为以下几步
-
先执行同步代码,如果执行中遇到了微任务就加入到微任务队列,遇到宏任务就加入宏任务队里 -
同步代码执行完之后,将微任务队列的任务依次执行,如果执行过程中产生新的微任务,则将微任务加入到微任务队列末尾,如果产生宏任务,则将宏任务加入到宏任务队列末尾。 -
当微任务队列执行完之后,就从宏任务队列中取出第一个开始执行 -
在宏任务里面,又开始从步骤1开始依次执行,形成一个循环
了解完宏任务与微任务的执行顺序之后,我们来一起看看今天的每日一题,挑战一下自己。
今日题目
请写出以下代码输入内容的顺序
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);