vlambda博客
学习文章列表

每日一题之Event Loop输出顺序

大家都知道,我们写的js代码会运行在单线程的环境中(虽然浏览器非单线程),而单线程很容易出现线程阻塞的问题,因此前端就有了异步的概念,而提到异步就不得不说到Event Loop,各大互联网公司在面试的时候,经常会问到Event Loop相关的知识,而今天的每日一题,便是一道与此相关的题目,一起来看看吧。

基础知识讲解

在我们日常写代码的时候,如果遇到请求数据,一般会通过axiosfetch发送一个异步请求,然后继续执行其他代码,等异步请求回来之后,再去执行异步请求的回调函数,这个过程是如何管理的呢?

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,为什么是这样的顺序呢?

看一下具体执行步骤:

  1. 代码执行时候首先遇到 console.log('start'),因为是同步代码,所以直接输出
  2. 然后遇到 setTimeout,通过上文可以知道 setTimout属于宏任务,加入到宏任务队列
  3. 然后遇到 new Promise, 上文说道 Promise.then属于微任务,但 new Promise却是同步代码,所以输出 promise,同时将 then加入到微任务队列
  4. 遇到 console.log('end'),直接输出
  5. 这一步很关键,当同步代码执行完成之后,首先会先将微任务队列里面的内容逐一执行,所以输出 then,同时需要注意,如果微任务里面还有微任务,会同时加入到微任务队列,继续执行
  6. 等到微任务执行完之后,就从宏任务队列里面取出来第一个继续执行,所以输出 setTimeout

上面的步骤就是一个完整的执行过程,然后如果宏任务里面又有新的同步代码与微任务,就从1开始重新执行一遍,这个过程被称为eventloop,整个过程可以分为以下几步

  1. 先执行同步代码,如果执行中遇到了微任务就加入到微任务队列,遇到宏任务就加入宏任务队里
  2. 同步代码执行完之后,将微任务队列的任务依次执行,如果执行过程中产生新的微任务,则将微任务加入到微任务队列末尾,如果产生宏任务,则将宏任务加入到宏任务队列末尾。
  3. 当微任务队列执行完之后,就从宏任务队列中取出第一个开始执行
  4. 在宏任务里面,又开始从步骤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);