Javascript 中的 Event Loop

前言

Event Loop即事件循環(huán),是指瀏覽器或Node的一種解決javaScript單線程運行時不會阻塞的一種機制,也就是我們經(jīng)常使用異步的原理。

什么是 Event Loop?

image.png

Event Loop 是一個很重要的概念,指的是計算機系統(tǒng)的一種運行機制。

想要理解Event Loop,就要從程序的運行模式講起。運行以后的程序叫做"進(jìn)程"(process),一般情況下,一個進(jìn)程一次只能執(zhí)行一個任務(wù)。

如果有很多任務(wù)需要執(zhí)行,不外乎三種解決方法。

(1)排隊。因為一個進(jìn)程一次只能執(zhí)行一個任務(wù),只好等前面的任務(wù)執(zhí)行完了,再執(zhí)行后面的任務(wù)。

(2)新建進(jìn)程。使用fork命令,為每個任務(wù)新建一個進(jìn)程。

(3)新建線程。因為進(jìn)程太耗費資源,所以如今的程序往往允許一個進(jìn)程包含多個線程,由線程去完成任務(wù)。

以JavaScript語言為例,它是一種單線程語言,所有任務(wù)都在一個線程上完成,即采用上面的第一種方法。一旦遇到大量任務(wù)或者遇到一個耗時的任務(wù),網(wǎng)頁就會出現(xiàn)"假死",因為JavaScript停不下來,也就無法響應(yīng)用戶的行為。

你也許會問,JavaScript為什么是單線程,難道不能實現(xiàn)為多線程嗎?
這跟歷史有關(guān)系。JavaScript從誕生起就是單線程。原因大概是不想讓瀏覽器變得太復(fù)雜,因為多線程需要共享資源、且有可能修改彼此的運行結(jié)果,對于一種網(wǎng)頁腳本語言來說,這就太復(fù)雜了。后來就約定俗成,JavaScript為一種單線程語言。(Worker API可以實現(xiàn)多線程,但是JavaScript本身始終是單線程的。)

Event Loop 是什么?

Wikipedia中對Event Loop的描述為:

在計算機科學(xué)中,Event Loop是指一個用于等待和發(fā)送消息和事件的程序結(jié)構(gòu)(In computer science, the 
event loop is a programming construct or design pattern that waits for and dispatches events 
or messages in a program. )

簡單來說,就是在除主線程之外設(shè)置一個用于協(xié)調(diào)主線程中和其他線程的一個“輔助線程”的結(jié)構(gòu)

Event Loop的具體內(nèi)容

在完整講述Event Loop之前,需要先講解一下Javascript的運行機制;在javascript中,代碼(程序)的執(zhí)行一般遵循一下的機制:

  1. 所有同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)
  2. 主線程之外還存在“任務(wù)隊列/調(diào)用隊列”(task queue),用于存放等待執(zhí)行的任務(wù)
  3. 每當(dāng)“執(zhí)行棧”中的同步任務(wù)執(zhí)行完成后,主線程將會將“任務(wù)隊列”中的第一個任務(wù)加入“執(zhí)行?!?/li>
v2-55c58a901cf835e5e308e4360694505f_720w.jpg

執(zhí)行棧

當(dāng)我們調(diào)用一個方法的時候,js會生成一個與這個方法相對應(yīng)的執(zhí)行環(huán)境,也叫執(zhí)行上下文,這個執(zhí)行環(huán)境存在著這個方法的私有作用域、參數(shù)、this對象等等。因為js是單線程的,同一時間只能執(zhí)行一個方法,所以當(dāng)一系列的方法被依次調(diào)用的時候,js會先解析這些方法,把其中的同步任務(wù)按照執(zhí)行順序排隊到一個地方,這個地方叫做執(zhí)行棧。

任務(wù)隊列

當(dāng)我們發(fā)出一個ajax請求,他并不會立刻返回結(jié)果,為了防止瀏覽器出現(xiàn)假死或者空白,主線程會把這個異步任務(wù)掛起(pending),繼續(xù)執(zhí)行執(zhí)行棧中的其他任務(wù),等異步任務(wù)返回結(jié)果后,js會將這個異步任務(wù)按照執(zhí)行順序,加入到與執(zhí)行棧不同的另一個隊列,也就是事件隊列。

以上的事件循環(huán)過程只是一個宏觀的表述,實際上異步任務(wù)之間也不相同,執(zhí)行優(yōu)先級也有區(qū)別。不同的異步任務(wù)被分為兩類:宏任務(wù)(macro task)和微任務(wù)(micro task)。我們將經(jīng)常遇到的異步任務(wù)進(jìn)行分類如下:

在javascript中,任務(wù)被分為兩種,一種宏任務(wù)(MacroTask)也叫Task,一種叫微任務(wù)(MicroTask)。

其中,宏任務(wù)指的是:

  • script全部代碼
  • setTimeout
  • setInterval
  • setImmediate(瀏覽器暫時不支持,只有IE10支持,具體可見MDN
  • I/O
  • UI Rendering

微任務(wù)指:

  • Process.nextTick(Node獨有)
  • Promise
  • Object.observe(廢棄)
  • MutationObserver(具體使用方式查看這里

按照前面的介紹,異步任務(wù)會按照執(zhí)行完成的順序添加到任務(wù)隊列中;事實上,根據(jù)異步任務(wù)的不同類型,這個任務(wù)最終會分別添加到宏任務(wù)隊列和微任務(wù)隊列中去

當(dāng)執(zhí)行棧中的任務(wù)清空,主線程會先檢查微任務(wù)隊列中是否有任務(wù),如果有,就將微任務(wù)隊列中的任務(wù)依次執(zhí)行,直到微任務(wù)隊列為空,之后再檢查宏任務(wù)隊列中是否有任務(wù),如果有,則每次取出第一個宏任務(wù)加入到執(zhí)行棧中,之后再清空執(zhí)行棧,檢查微任務(wù),以此循環(huán)... ...

簡單來說,就是微任務(wù)隊列中的所有任務(wù)的優(yōu)先級都高于宏任務(wù)隊列中的任務(wù)

v2-4f6d315afb5bab4f7ab66be4eb6f7ffb_720w.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容