JS事件循环以及宏任务与微任务
先看一道简单题目
console.log(1)
setTimeOut(() => console.log(2), 0);
console.log(3)
相信大部分人的回答都是1,3,2,那这是为什么呢?
其实这是JS的异步原理,先来看一下下面这张图…
我们都知道JavaScript是一门单线程语言,单线程就是说JS在执行代码的时候只有一个主线程来处理任务,而我们有知道JS可以执行异步任务,JS执行异步任务正是通过事件循环机制Event Loop实现的
我们再来看上面这张图,上面这张图非常直观的展示了事件循环机制的过程:
一段JS代码开始执行时,会将其中的同步代码按照顺序推入JS主线程的调用栈,然后从头开始执行,当遇到异步任务例如DOM事件,ajax和setTimeOut时,会交给WebAPI执行,并继续执行主线程的同步代码。WebAPI在执行完异步任务后,会将异步任务的回调函数(例如OnClick事件,ajax请求的成功或失败,还有setTimeOut的回调函数等)交给任务队列,当JS主线程的同步任务执行完毕后,会查询任务队列,取出一个任务并执行,并重复这个动作,这就叫事件循环
我们再来看上面那段代码:
JS首先会执行console.log(1),把它推入调用栈中并执行打印1,执行完成后出栈,继续向下执行setTimeOut(() => console.log(2), 0),将这段代码推入WebAPI中,WebAPI将setTimeOut的回调函数推入任务队列中,JS主线程继续执行console.log(3),执行完毕后将任务队列中setTimeOut回调函数中的的console.log(2)推入调用栈并执行。
也就是说,JS在执行一段代码时,如果遇到了一个异步任务,JS不会一直等待它的结果返回,而是在遇到了异步任务时将它交给别人(Web API),JS在将自己调用栈中的所有同步任务执行完毕后,主线程处于闲置状态,这时候再从任务队列中按顺序依次推入调用栈中执行,完成后重复这个过程
宏任务与微任务
除了将JS的任务定义为同步任务与异步任务外,JS还将任务分为宏任务与微任务,不同类型的任务会进入对应的任务队列中
常见的宏任务:setTimeOut、setInterval,DOM事件,ajax请求
常见的微任务:Promise,async/await
来看下面一段代码,看看打印出来的结果是什么:
console.log(1)
setTimeOut(() => console.log(2), 0);
Promise.resolve().then(res => {
console.log(3)
})
console.log(4)
打印出来的结果为1,4,3,2。这是为什么呢?让我们带着疑问寻找答案
JS在拿到这段代码时首先执行了同步代码console.log
,这是毫无疑问的,那2和3的顺序应该怎么区分呢?由于setTimeOut
和Promise
都是异步任务,JS在遇到setTimeOut交给WebAPI,webAPI在过了执行的时间后将回调函数放到宏任务的任务队列中,然后向下执行遇到Promise,将then函数放到微任务的任务队列中,整个调用栈的任务执行完毕后,JS会首先检查微任务的任务队列中是否有任务,有就执行,执行完毕后到宏任务的任务队列中执行宏任务,并重复这个动作
也就是说,在JS中,微任务的执行顺序先于宏任务
学习了事件循环后让我受益匪浅,尤其是理解了事件循环机制后对JS代码的执行顺序有了更进一步的认识,面试遇到这种问题 / 代码的时候可以手撕不会又是似懂非懂的样子了
推荐阅读
ps:建议阅读完《这一次,彻底弄懂 JavaScript 执行机制》后把最后的代码做一遍检查自己是否真的搞懂了事件循环机制
ps:面试重点! !"我的朋友"面字节暑期实习遇到的原题就是“先说说事件循环机制,宏任务和微任务”