Event Loop 其实也就这点事
前段時(shí)間在網(wǎng)上陸續(xù)看了很多關(guān)于 Event loop 的文章,看完也就混個(gè)眼熟,可能內(nèi)心深處對(duì)這種偏原理的知識(shí)有一些抵觸心情,看完后也都沒有去深入理解。最近在看 Vue 的源碼,在讀到關(guān)于 nextTick 的實(shí)現(xiàn)時(shí),總有一種似曾相識(shí)的感覺,于是去網(wǎng)上查了下資料,原來 nextTick 的實(shí)現(xiàn)正是基于 Event loop 機(jī)制(引起重視了)。
Call Stack
眾所周知,JavaScript 是 one-threaded,也就意味著在執(zhí)行 JavaScript 的過程中,是 One thing at a time,而這樣的特性,正是由一個(gè)叫 Call Stack 的東西決定的(有且僅有一個(gè))。
既然是棧,就滿足 FILO 的原則。故 Call Stack 在函數(shù)運(yùn)行時(shí)的表現(xiàn)為:
- 當(dāng)有函數(shù)執(zhí)行時(shí),該函數(shù)被 push 到 Call Stack
- 當(dāng)函數(shù)執(zhí)行結(jié)束時(shí),該函數(shù)從 Call Stack 內(nèi)被 pop 出
- 如果函數(shù)內(nèi)有調(diào)用到其他函數(shù)(執(zhí)行結(jié)束前),則將其他函數(shù)再 push 到 Call Stack 中,等到調(diào)用結(jié)束時(shí) pop 出
由此可見,如果一個(gè)函數(shù)定義如下:
const dead = () => {return dead(); }那么當(dāng)其被執(zhí)行時(shí),就會(huì)向 Call Stack 中不斷的 push 同一個(gè)函數(shù)(dead),導(dǎo)致整個(gè)頁(yè)面掛掉。
When Call Stack Meets Sync Request
眾所又周知了,在 jQuery 提供的 Ajax 函數(shù)中,可供開發(fā)者選擇請(qǐng)求是 sync 還是 async,我們先討論 Call Stack 遇到 sync 請(qǐng)求時(shí)會(huì)發(fā)生什么。
const name = $.ajaxSync(URL_1); const info = $.ajaxSync(URL_2); const work = $.ajaxSync(URL_3);console.log(name); console.log(info); console.log(work);屋漏偏逢連陰雨,此時(shí)的網(wǎng)絡(luò)狀態(tài)又極差,每一個(gè)網(wǎng)絡(luò)請(qǐng)求從發(fā)出到成功要經(jīng)歷五秒,想象一下上面這段代碼如果跑起來了,會(huì)發(fā)生什么?
這是一件讓人絕望的事情:
隨著程序的推進(jìn),ajaxSync 函數(shù)會(huì)先后三次被 push 并 pop 出 Call Stack,而每一次從 push 到 pop 的過程需要耗費(fèi)五秒鐘的時(shí)間。
無論從工程效率還是用戶體驗(yàn)的角度來說,這都是不被允許的一件事情。
Async & Callback
為了杜絕上面的問題,瀏覽器提供給了開發(fā)者一個(gè)叫做異步 Callback 的解決方案。先看一段代碼:
console.log('kyrieliu');setTimeout(function(){console.log('about Event Loop'); }, 5000);console.log(' is writing an article ');運(yùn)行結(jié)果顯而易見。
ok,那么這段代碼在 Call Stack 中的表現(xiàn)又是怎樣的呢?
基于上面文章所述,我推測(cè):
首先,第一行代碼入棧,執(zhí)行完畢后出棧;緊接著,setTimeout 入棧,然后emmm,事情有點(diǎn)不對(duì)勁了:如果 setTimeout 入棧執(zhí)行后立刻出棧,那么它內(nèi)部的 console 為什么五秒后才打印出來?
Task Queue
問題的關(guān)鍵,是一個(gè)叫做 Task Queue 的東西。
緊接著剛才的步驟:setTimeout入棧后執(zhí)行并觸發(fā)了一個(gè)五秒的 timer,這個(gè) timer 由 Web api 維護(hù),至此,setTimeout執(zhí)行完畢并出棧,第三個(gè) console 入棧執(zhí)行并出棧。五秒后,timer 結(jié)束計(jì)時(shí),將回調(diào)函數(shù) callback 下放到 task queue 中。
但 callback 還未執(zhí)行,它什么時(shí)候執(zhí)行呢?Call Stack 為空的時(shí)候。
此時(shí)的 call stack 已經(jīng)為空,所以 callback 被 push 進(jìn)棧執(zhí)行并 pop 出,這樣一來就解釋得通了。 至此,正式引出 Event Loop 的概念。
Event Loop
If the call stack is clear and there's something in the task queue, push the first thing on the queue onto the stack.
setTimeout(callback, 0)
在最開始接觸 JavaScript 的時(shí)候,看到上面這行代碼的我是懵蔽的,0ms 后執(zhí)行 callback, WTF?
在了解了 Event Loop 的運(yùn)行機(jī)制后,再回過頭來嘗試解釋一下這行代碼,即:在 setTimeout 入棧執(zhí)行時(shí),內(nèi)部的 callback 會(huì)立即被下放到 task queue 中,但它無法執(zhí)行,因?yàn)榇藭r(shí)的 call stack 不為空,等到 call stack 為空時(shí),callback 才得以執(zhí)行。
Thanks
- Philip Roberts: Help, I’m stuck in an event-loop.
- A super-cool demo of Event Loop
廣而告之
個(gè)人公眾號(hào),不止于前端
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的Event Loop 其实也就这点事的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WePY:在质疑中前进 | 文末福利
- 下一篇: 小程序各种姿势实现登录