前言

由于javaScript作为脚本语言,主要用途是与用户互动,以及操作浏览器DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?这就导致了javaScript注定成为一门单线程的脚本语言,没办法多线程执行任务。

什么是event loop(事件循环)?

是指浏览器Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
查看任务队列中是否存在任务,存在则送到执行栈中执行,反复循环查看并执行,这个过程称之为事件循环(event loop)。

js的任务分为同步任务异步任务,而异步任务里面又分宏任务微任务

为啥需要知道event loop?

  • 一是要增加自己技术的深度,也就是懂得JavaScript的运行机制。
  • 二是现在前端领域各种技术层出不穷,掌握底层原理,知道其本质。可以让自己以不变,应万变。
  • 三是应对各大互联网公司的面试,懂其原理,题目任其发挥。

案例

看个的经典的代码段,并说明打印的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log(1)

setTimeout(() => {
console.log(2)
}, 0)

new Promise((resolve, reject)=>{
resolve()
console.log(3)
}).then(res =>{
console.log(4)
})

console.log(5)

上面这段代码输出的正确顺序为:1、3、5、4、2。

原因:setTimeout宏任务new Promise then/catch微任务/异步任务;其他的为同步任务。具体可以看看下面的详细解释,相信你看完过后将会更明白,别人问起将不再吞吐。

宏任务

script标签中的全部代码、setTimeoutsetIntervalsetImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDNI/OUI Rendering

微任务

Promise 中的then/catchasync / awaitProcess.nextTick(Node独有)、Object.observe(废弃)、MutationObserver(具体使用方式查看这里)

宏任务和微任务执行图表说明

执行栈中会先执行同步任务,同步任务执行完后会执行微任务,微任务执行完后会执行宏任务,当一个宏任务执行后会看执行栈中是否存在同步任务,存在则先执行同步任务,同步任务执行完后会查看是否存在微任务,存在则执行微任务,如果不存在同步任务和微任务则继续宏任务,这个过程是一个循环往复的过程。如果宏任务中存在同步任务和微任务则先执行,并重复刚刚的步骤。

image.png

结合上面的案例看,相信你会更加深刻。

同步任务

放入js主(线程/引擎)中立即执行并原地等待结果的任务。有consolenew Promise的函数体等。

异步任务

先放入宿主环境(浏览器、node)中,不必原地等待结果的任务,在将来执行,并将执行的回调函数放入主线程,不影响/阻塞主线程继续往下执行的任务。有setTimeoutAjax/FetchsetInterval注册事件回调(如:addEventListener)Promise的then/catch

同步任务和异步任务图表说明

当执行栈中的的任务执行完后会清除一次执行栈,并重新从任务队列中拿取任务并执行(异步任务)。
创建完同步任务后会直接放进执行栈并原地等待执行结果;
异步任务创建时会先放进宿主环境,带时机成熟(比如定时器的时间到了)会将异步任务回调放入任务队列中,然后执行栈会根据任务队列中的任务进行执行。

image.png

结合上面的案例看,相信你会更加深刻。

我们将事件查找并执行的这样一个循环过程称之为事件循环,也叫 event loop

总结

  • js 是单线程的,防止代码执行过程中的阻塞,我们把任务分为了:同步任务和异步任务
  • 同步任务将会交给js引擎执行,异步任务将交给宿主环境执行
  • 同步任务将放入执行栈立即执行并原地等待结果,异步任务会待时机成熟时推入任务队列排队执行
  • 执行栈执行完毕时,会清空执行栈并查看任务队列中是否存在任务,存在则送到执行栈中执行,反复循环查看并执行,这个过程称之为事件循环(event loop)

感谢你阅读本篇文章,希望本篇文章对你有所帮助!