JS事件队列执行顺序

404 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

前言

我们都知道JavaScript是单线程的,也就是说Js代码只能在一个线程上运行,每次最多只能同时执行一个JS任务。若有多个任务要执行,则必须排队按照队列来执行(前一个任务完成,再执行下一个任务)。

虽然Javascript是单线程的,但浏览器的多线程的,因此像点击事件的onClicksetTimeoutAjax等是依赖于浏览器的node宿主环境来实现的。

JavaScript执行顺序是从上往下的,遇到异步代码则放入到事件队列内。而事件队列又包含宏任务和微任务。

宏任务:

  • setTimeout: 浏览器和node中都是宏任务
  • setInterval:浏览器和node中都是宏任务
  • setImmediate: 浏览器中不是,node中是
  • Ajax:浏览器和node中都是宏任务
  • DOM事件:浏览器和node中都是宏任务

微任务:

  • MutationObserver: 浏览器中是微任务,node中不是
  • Promise.then catch finall: 浏览器和node中都是微任务

首先,我们需要明白一点的是,JavaScript执行流程是:同步代码->微任务->宏任务,只有同步代码执行完了才会在事件队列内取出微任务进行执行,等到所有微任务执行完结后,才开始执行宏任务。

明白JS的执行顺序后,我们来尝试做以一下面试题吧(狗头保命)。

async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout') 
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
}).then(function(){
    console.log('promise2')
})
console.log('script end')

各位读者可以先做一下,看看自己对JS事件队列内的宏任务微任务的了解程度。这里我理解的输出是: script start->async1 start->async2 ->async1 end->promise1->script end->promise2

但其实正确的输出结果是:script start->async1 start->async2->promise1->script end->async1 end->promise2

这里我之所以做错了是因为我遗忘了一个小点!async函数返回值是一个Promise,而async2前面又加了await关键词,因此async2执行await async2()就是执行一个微任务,因此async1 end执行会慢过promise1