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

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í)行一般遵循一下的機制:
- 所有同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)
- 主線程之外還存在“任務(wù)隊列/調(diào)用隊列”(task queue),用于存放等待執(zhí)行的任務(wù)
- 每當(dāng)“執(zhí)行棧”中的同步任務(wù)執(zhí)行完成后,主線程將會將“任務(wù)隊列”中的第一個任務(wù)加入“執(zhí)行?!?/li>

執(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全部代碼 setTimeoutsetInterval-
setImmediate(瀏覽器暫時不支持,只有IE10支持,具體可見MDN) I/OUI Rendering
微任務(wù)指:
Process.nextTick(Node獨有)PromiseObject.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ù)
