摘要:心塞塞根據(jù)規(guī)范,事件循環(huán)是通過(guò)任務(wù)隊(duì)列的機(jī)制來(lái)進(jìn)行協(xié)調(diào)的。等便是任務(wù)源,而進(jìn)入任務(wù)隊(duì)列的是他們指定的具體執(zhí)行任務(wù)回調(diào)函數(shù)。然后當(dāng)前本輪的結(jié)束,主線(xiàn)程可以繼續(xù)取下一個(gè)執(zhí)行。
依然是:經(jīng)濟(jì)基礎(chǔ)決定上層建筑。
說(shuō)明首先,旨在搞清常用的同步異步執(zhí)行機(jī)制
其次,暫時(shí)不討論node.js的Event Loop執(zhí)行機(jī)制,以下關(guān)于瀏覽器的Event Loop執(zhí)行機(jī)制
最后,借鑒了很多前輩的研究文章,非常感謝,此文主要是梳理所學(xué),還請(qǐng)保持質(zhì)疑以追求正確的知識(shí)
要點(diǎn)基本概念
同步異步操作
Event Loop
基本概念先解釋現(xiàn)代js引擎幾個(gè)概念。
stack(棧):這里放著js正在執(zhí)行的任務(wù)。理解事件循環(huán)一(淺析)一文有對(duì) stack 的 example 解釋。
heap(堆):一個(gè)用來(lái)表示內(nèi)存中一大片非結(jié)構(gòu)化區(qū)域的名字,對(duì)象都被分配在這。
queue(隊(duì)列):一個(gè) js runtime 包含了一個(gè)任務(wù)隊(duì)列,該隊(duì)列是由一系列待處理的任務(wù)組成。而每個(gè)任務(wù)都有相對(duì)應(yīng)的函數(shù)。當(dāng)棧為空時(shí),就會(huì)從任務(wù)隊(duì)列中取出一個(gè)任務(wù),并處理之。當(dāng)該任務(wù)處理完畢后,棧就會(huì)再次為空。(queue的特點(diǎn)是先進(jìn)先出(FIFO))。
為了方便描述與理解,作出以下約定:
stack 棧為主線(xiàn)程
queue 隊(duì)列為任務(wù)隊(duì)列(等待調(diào)度到主線(xiàn)程執(zhí)行)
同步異步js 是一門(mén)單線(xiàn)程語(yǔ)言。 js 引擎有一個(gè)主線(xiàn)程(main thread)用來(lái)解釋和執(zhí)行 js 程序,實(shí)際上還存在其他的線(xiàn)程。例如:處理AJAX請(qǐng)求的線(xiàn)程、處理DOM事件的線(xiàn)程、定時(shí)器線(xiàn)程、讀寫(xiě)文件的線(xiàn)程(例如在node.js中)等等。這些線(xiàn)程可能存在于 js 引擎之內(nèi),也可能存在于 js 引擎之外,在此我們不做區(qū)分。不妨叫它們工作線(xiàn)程。但是前輩們頗有一種小本本記好的說(shuō)法,那就是,要相信 js 單線(xiàn)程的本質(zhì),其他一切看似多線(xiàn)程,都是紙老虎。哈哈哈哈哈哈哈哈哈哈哈哈哈......
任務(wù)分為同步任務(wù)(synchronous)和異步任務(wù)(asynchronous),如果所有任務(wù)都由主線(xiàn)程來(lái)處理,會(huì)出現(xiàn)主線(xiàn)程被阻塞而使得頁(yè)面“假死”。為了主線(xiàn)程不被阻塞,異步任務(wù)(如:AJAX異步請(qǐng)求,定時(shí)器等)就會(huì)交給工作線(xiàn)程來(lái)處理,異步任務(wù)完成后將異步回調(diào)函數(shù)注冊(cè)進(jìn)任務(wù)隊(duì)列,等待主線(xiàn)程空閑時(shí)調(diào)用。流程如圖:
// example console.log("example-start") setTimeout(() => { console.log("setTimeout-0") }, 0) console.log("example-end") /* chrome result * example-start example-end setTimeout-0 * */
上面一個(gè)簡(jiǎn)單的小 js 片段的執(zhí)行過(guò)程:
主線(xiàn)程開(kāi)始同步任務(wù)執(zhí)行,執(zhí)行console.log("example-start")
然后接下來(lái),主線(xiàn)程遇見(jiàn)一個(gè)異步操作setTimeout,將改異步任務(wù)交給工作線(xiàn)程處理,異步任務(wù)完成之后,將回調(diào)函數(shù)注冊(cè)進(jìn)任務(wù)隊(duì)列,等待被調(diào)用
繼續(xù)同步任務(wù)處理,執(zhí)行console.log("example-end")
主線(xiàn)程空閑,調(diào)用任務(wù)隊(duì)列中等待執(zhí)行的回調(diào)函數(shù),執(zhí)行console.log("setTimeout-0")
最后借用Philip Roberts的生動(dòng)形象的一張圖,callback queue可以簡(jiǎn)單理解為任務(wù)隊(duì)列,詳細(xì)的下面會(huì)講。
然而Event Loop并沒(méi)有上面圖中描述那么簡(jiǎn)單。心塞塞 : (
根據(jù)規(guī)范,事件循環(huán)是通過(guò)任務(wù)隊(duì)列的機(jī)制來(lái)進(jìn)行協(xié)調(diào)的。一個(gè) Event Loop 中,可以有一個(gè)或者多個(gè)任務(wù)隊(duì)列(task queue),一個(gè)任務(wù)隊(duì)列便是一系列有序任務(wù)(task)的集合;每個(gè)任務(wù)都有一個(gè)任務(wù)源(task source),源自同一個(gè)任務(wù)源的 task 必須放到同一個(gè)任務(wù)隊(duì)列,從不同源來(lái)的則被添加到不同隊(duì)列。
setTimeout/Promise 等API便是任務(wù)源,而進(jìn)入任務(wù)隊(duì)列的是他們指定的具體執(zhí)行任務(wù)(回調(diào)函數(shù))。來(lái)自不同任務(wù)源的任務(wù)會(huì)進(jìn)入到不同的任務(wù)隊(duì)列。其中setTimeout與setInterval是同源的。
仔細(xì)查閱規(guī)范可知,異步任務(wù)可分為 task(部分文章也稱(chēng)為 macro-task) 和 micro-task 兩類(lèi),不同的API注冊(cè)的異步任務(wù)會(huì)依次進(jìn)入自身對(duì)應(yīng)的隊(duì)列中,然后等待 Event Loop 將它們依次壓入執(zhí)行棧中執(zhí)行。
task主要包含:script(整體代碼)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(node.js 環(huán)境)
micro-task主要包含:Promise.then、MutaionObserver、MessageChannel、process.nextTick(node.js 環(huán)境)
在事件循環(huán)中,每進(jìn)行一次循環(huán)操作稱(chēng)為 tick,每一次 tick 的任務(wù)處理模型是比較復(fù)雜的,但關(guān)鍵步驟如下:
在此次 tick 中選擇最先進(jìn)入隊(duì)列的任務(wù)(oldest task),如果有則執(zhí)行(一次)
檢查是否存在 micro-task,如果存在則不停地執(zhí)行,直至清空 micro-task queue
更新 render
主線(xiàn)程重復(fù)執(zhí)行上述步驟
一個(gè)事件循環(huán)(Event Loop)中,主線(xiàn)程從任務(wù)隊(duì)列中取出一個(gè)任務(wù) task 執(zhí)行時(shí),而這個(gè)正在執(zhí)行的任務(wù)就是從 task queue(部分文章也稱(chēng)為 macro-task queue)中來(lái)的。當(dāng)這個(gè) task 執(zhí)行結(jié)束后,js 會(huì)將 micro-task queue中所有 micro-task 都在同一個(gè) Event Loop 中執(zhí)行,當(dāng)這些 micro-task 執(zhí)行結(jié)束后還能繼續(xù)添加 micro-task 一直到整個(gè) micro-task 隊(duì)列執(zhí)行結(jié)束。然后當(dāng)前本輪的 Event Loop 結(jié)束,主線(xiàn)程可以繼續(xù)取下一個(gè) task 執(zhí)行。所以更詳細(xì)的 Event Loop 的流程圖如下:
// example console.log("example-start") setTimeout(() => { console.log("setTimeout-0") // setTimeout-1 }, 0) new Promise((resolve, reject) => { console.log("promise-1") resolve("promise-2") Promise.resolve().then(() => console.log("promise-3")) // then-1 }).then((response) => { // then-2 console.log(response) setTimeout(() => { console.log("setTimeout-10") // setTimeout-2 }, 10) }) console.log("example-end") /* chrome result * example-start promise-1 example-end promise-3 promise-2 setTimeout-0 setTimeout-10 * */
上面一個(gè)簡(jiǎn)單的 js 片段的執(zhí)行過(guò)程:
第一輪事件循環(huán):
第二輪事件循環(huán):
第三輪事件循環(huán):
如果上文理解有誤或者有疑惑,歡迎交流。
參考Philip Roberts: Help, I’m stuck in an event-loop.
JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop
關(guān)于JavaScript單線(xiàn)程的一些事
從一道題淺說(shuō) JavaScript 的事件循環(huán)
Event Loop的規(guī)范和實(shí)現(xiàn)
這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制
好記性不如爛筆頭。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95264.html
摘要:深入理解引擎的執(zhí)行機(jī)制靈魂三問(wèn)為什么是單線(xiàn)程的為什么需要異步單線(xiàn)程又是如何實(shí)現(xiàn)異步的呢中的中的說(shuō)說(shuō)首先請(qǐng)牢記點(diǎn)是單線(xiàn)程語(yǔ)言的是的執(zhí)行機(jī)制。 深入理解JS引擎的執(zhí)行機(jī)制 1.靈魂三問(wèn) : JS為什么是單線(xiàn)程的? 為什么需要異步? 單線(xiàn)程又是如何實(shí)現(xiàn)異步的呢? 2.JS中的event loop(1) 3.JS中的event loop(2) 4.說(shuō)說(shuō)setTimeout 首先,請(qǐng)牢記2...
摘要:曾經(jīng)的理解首先,是單線(xiàn)程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么為什么不是多線(xiàn)程呢這樣還能提高效率啊假定同時(shí)有兩個(gè)線(xiàn)程,一個(gè)線(xiàn)程在某個(gè)節(jié)點(diǎn)上編輯了內(nèi)容,而另一個(gè)線(xiàn)程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器就很懵逼了,到底以執(zhí)行哪個(gè)操作呢所以,設(shè)計(jì)者把 Event Loop曾經(jīng)的理解 首先,JS是單線(xiàn)程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么 為什么JavaScript不是多線(xiàn)程呢?這樣還能提...
摘要:主線(xiàn)程在任務(wù)隊(duì)列中讀取事件,這個(gè)過(guò)程是循環(huán)不斷地,所以這種運(yùn)行機(jī)制叫做事件循環(huán)是在執(zhí)行棧同步代碼結(jié)束之后,下一次任務(wù)隊(duì)列執(zhí)行之前。 單線(xiàn)程 javascript為什么是單線(xiàn)程語(yǔ)言,原因在于如果是多線(xiàn)程,當(dāng)一個(gè)線(xiàn)程對(duì)DOM節(jié)點(diǎn)做添加內(nèi)容操作的時(shí)候,另一個(gè)線(xiàn)程要?jiǎng)h除這個(gè)DOM節(jié)點(diǎn),這個(gè)時(shí)候,瀏覽器應(yīng)該怎么選擇,這就造成了混亂,為了解決這類(lèi)問(wèn)題,在一開(kāi)始的時(shí)候,javascript就采用單線(xiàn)...
摘要:深入理解引擎的執(zhí)行機(jī)制最近在反省,很多知識(shí)都是只會(huì)用,不理解底層的知識(shí)。在閱讀之前,請(qǐng)先記住兩點(diǎn)是單線(xiàn)程語(yǔ)言的是的執(zhí)行機(jī)制。所以,是存在異步執(zhí)行的,比如單線(xiàn)程是怎么實(shí)現(xiàn)異步的場(chǎng)景描述通過(guò)事件循環(huán),所以說(shuō),理解了機(jī)制,也就理解了的執(zhí)行機(jī)制啦。 深入理解js引擎的執(zhí)行機(jī)制 最近在反省,很多知識(shí)都是只會(huì)用,不理解底層的知識(shí)。所以在開(kāi)發(fā)過(guò)程中遇到一些奇怪的比較難解決的bug,在思考的時(shí)候就會(huì)收...
摘要:前言前幾天在理解的事件環(huán)機(jī)制中引發(fā)了我對(duì)瀏覽器里的好奇。接下來(lái)理解瀏覽器中的,先看一張圖堆和棧堆是用戶(hù)主動(dòng)請(qǐng)求而劃分出來(lái)的內(nèi)存區(qū)域,比如你,就是將一個(gè)對(duì)象存入堆中,可以理解為存對(duì)象。廢話(huà)不多說(shuō),直接上圖個(gè)人理解。參考資料運(yùn)行機(jī)制詳解再談 前言 前幾天在理解node的事件環(huán)機(jī)制中引發(fā)了我對(duì)瀏覽器里Event Loop的好奇。我們都知道javascript是單線(xiàn)程的,任務(wù)是需要一個(gè)一個(gè)按順...
閱讀 2385·2021-11-18 10:07
閱讀 2336·2021-09-22 15:59
閱讀 3095·2021-08-23 09:42
閱讀 2296·2019-08-30 15:44
閱讀 1208·2019-08-29 15:06
閱讀 2335·2019-08-29 13:27
閱讀 1230·2019-08-29 13:21
閱讀 1431·2019-08-29 13:13