-
js是單線程的荆针?與異步矛盾嘛碑宴?
不矛盾,因?yàn)閖s執(zhí)行是單線程的姐叁,但是瀏覽器是多線程的瓦盛。其中的異步是靠瀏覽器是開新的進(jìn)程去執(zhí)行的。其中是依靠libuv
其的單線程是:一個(gè)瀏覽器中只有一個(gè)js的執(zhí)行線程外潜,同一時(shí)刻也只有一個(gè)js文件在執(zhí)行谭溉,其會(huì)阻塞其他任務(wù)的進(jìn)行。
先寫點(diǎn)東西橡卤,
瀏覽器是多線程的,一般會(huì)有以下幾個(gè)常駐的線程在運(yùn)行
- GUI render thread
- JS engine thread (也就是下文所說的主線程)
- asnyc http request thread
- setTimeout thread
- Event target thread
-
Event loop thread 這個(gè)是瀏覽器的主線程损搬,其是一個(gè)無限循環(huán)碧库,永遠(yuǎn)處于接受處理的狀態(tài),并等待事件(如布局和繪制事件)發(fā)生巧勤,并進(jìn)行處理嵌灰。
瀏覽器是事件驅(qū)動(dòng)的(event driven),其的很多行為會(huì)創(chuàng)建異步事件,然后將其加入到任務(wù)隊(duì)列中去颅悉。
當(dāng)js在解析過程中沽瞭,碰見一些異步情況,如Ajax剩瓶,setTimeout.etc驹溃,會(huì)
一個(gè)chrome瀏覽器的一個(gè)tab page中有一個(gè)不斷循環(huán)的線程,叫做Event loop延曙。其貫穿頁面的開啟和關(guān)閉豌鹤。還有相關(guān)若干的線程,如GUI render thread枝缔、JS engine thread 布疙、setTimeout thread、Event target thread愿卸、async http request thread灵临,其中GUI和js thread是相斥的,一個(gè)運(yùn)行會(huì)阻塞別一個(gè)運(yùn)行趴荸。然后因?yàn)閖s是單線程的儒溉,然后這個(gè)js engine thread 會(huì)調(diào)用一個(gè)子線程去運(yùn)行js代碼,然后其他的線程去忙別的事情赊舶。
單線程的js在解析代碼的時(shí)候睁搭,會(huì)將demo以call stack 進(jìn)行解析赶诊。在不斷的解析過程中,碰見異步的情況园骆,js去調(diào)用web的apis舔痪,這個(gè)時(shí)候,就是讓瀏覽器中其他的線程去完成異步情況锌唾。當(dāng)其他的線程完成異步情況之后锄码,將這個(gè)完成情況(也就是通常所說的消息,也指向回調(diào)函數(shù))插入任務(wù)隊(duì)列晌涕。然后等到執(zhí)行棧沒有任務(wù)的時(shí)候滋捶,eventLoop會(huì)將任務(wù)隊(duì)列的任務(wù)給推到執(zhí)行棧中進(jìn)行執(zhí)行。
同時(shí)也解釋了為什么setTimeout(function(){},0)余黎,也不能直接involve 回調(diào)函數(shù)重窟。因?yàn)閟etTimeout是個(gè)異步過程。只有當(dāng)call stack中的任務(wù)運(yùn)行結(jié)束之后惧财,才會(huì)在callback queue中去調(diào)用setTimeout的callback function巡扇。
reference
可視化js工作流程
reference
中文文檔和blog真的有時(shí)候不行,浪費(fèi)了我一天的時(shí)間去弄懂這些垮衷√瑁看完英語的,一個(gè)小時(shí)就搞懂了搀突。mmp刀闷。
Update ??
原來不光有Event Loop,還有microTask仰迁,macroTask甸昏,在去真的了解Promise才發(fā)現(xiàn)的。
下圖中的taskQueue就是macrotask徐许。
其中macroTask和microTask是兩種任務(wù)隊(duì)列筒扒,在上文中,是大致介紹javascript Engine的流程绊寻,但是并沒有對(duì)其中的task queue進(jìn)行劃分花墩。相比而言,大家更熟悉的一個(gè)詞是任務(wù)隊(duì)列(task queue,其實(shí)就是macroTask),大家更熟悉的關(guān)于事件循環(huán)的機(jī)制說法大概是:主進(jìn)程執(zhí)行完了之后澄步,每次從任務(wù)隊(duì)列里取一個(gè)任務(wù)執(zhí)行冰蘑。但是promise出現(xiàn)之后,這個(gè)說法就不太準(zhǔn)確了村缸。
JavaScript引擎對(duì)這兩種隊(duì)列有不同的處理祠肥,簡(jiǎn)單的說就是引擎會(huì)把我們的所有任務(wù)分門別類,一部分歸為macroTask梯皿,另外一部分歸為microTask仇箱,下面是類別劃分:
- macroTask: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering,平時(shí)的script中的代碼
- microTask: process.nextTick, Promise, Object.observe, MutationObserver
我們所熟悉的定時(shí)器就屬于macroTask县恕,但是僅僅了解macroTask的機(jī)制還是不夠的。
update:2018-8-25 其中在js中剂桥,對(duì)于macroTask和microTask的處理是不一樣的忠烛,如下圖
為此總結(jié)一句口訣:代碼運(yùn)行就是宏,遇見new Promise()就執(zhí)行权逗,then放microTask中后執(zhí)行美尸,process.nextTick()等microTask,看進(jìn)入taskQueue順序斟薇,畢竟是隊(duì)列师坎,先進(jìn)先出,setTimeout最后來堪滨。
強(qiáng)烈推薦看這個(gè)
promise/A+規(guī)范
reference
reference1
從Promise來看JavaScript中的Event Loop胯陋、Tasks和Microtasks
理解事件循環(huán)二(macrotask和microtask)
nodejs construction
帶你領(lǐng)略Nodejs前世今生