看看下面的一段代码?你觉得输出的是什么?
setTimeout(_ => console.log(4))
new Promise(resolve => {
resolve()
console.log(1)
}).then(_ => {
console.log(3)
})
console.log(2)
跟你想象的一样的嘛?
整个这一串代码我们所在的层级我们看做一个任务,其中我们先执行同步代码。第一行的 setTimeout 是异步代码,跳过,来到了 new Promise(…) 这一段代码。前面提到过,这种方式是一个构造函数,是一个同步代码,所以执行同步代码里面的函数,即 console.log(1),接下来是一个 then 的异步,跳过。最后一个是一段同步代码 console.log(2)。所以,这一轮中我们知道打印了1, 2两个值。接下来进入下一步,即之前我们跳过的异步的代码。从上往下,第一个是 setTimeout,还有一个是 Promise.then()。setTimeout 是宏任务的异步,Promise.then()是微任务的异步,微任务是优先于宏任务执行的,所以,此时会先跳过 setTimeout 任务,执行两个 Promise.then() 的微任务。所以此时会执行 console.log(3) 函数。最后就只剩下 setTimeout 函数没有执行,所以最后执行 console.log(4)。
综上:最后的执行结果是 1, 2, 3, 5, 4。
解释下:
setTimeout就是作为宏任务来存在的,而Promise.then则是具有代表性的微任务
Event-Loop是个啥
上边一直在讨论 宏任务、微任务,各种任务的执行。
但是回到现实,JavaScript是一个单进程的语言,同一时间不能处理多个任务,所以何时执行宏任务,何时执行微任务?我们需要有这样的一个判断逻辑存在。
micro task是macro task的一个步骤,那么应该是“一条macro task先执行”,然后是当前所有micro task逐条执行,然后是“下一条macro task执行”, 重复上面loop。备注,script标签是第一个要执行的macro task。
但是有一个问题,什么呢?可以看到,在浏览器中,会有一个 undefined 的返回值。为什么呢?这是因为浏览器将上面的一整段代码当成一个函数,而这个函数执行完成以后返回了 undefined。那么?这就完了吗?没有。我们看看浏览器返回的截图中,3 数字其实是在 undefined 前面。3数是 Promise.then() 中的 console.log() 的打印值,而 undefined 在这里可以作为一轮任务的结束。这表明的意思就是,微任务会在下一轮任务开始前执行。