JavaScript單線(xiàn)程和多線(xiàn)程是很多小白同學(xué)入門(mén)的時(shí)候問(wèn)到最多的問(wèn)題,雖然官方給出過(guò)解釋但對(duì)于新手來(lái)說(shuō)并不友好,今天小千就來(lái)給大家介紹一下JavaScript的單線(xiàn)程。
web前端培訓(xùn)1" />
一、 瀏覽器的進(jìn)程和線(xiàn)程
瀏覽器的架構(gòu)是多進(jìn)程的,不同的瀏覽器采用不同的進(jìn)程架構(gòu),這個(gè)沒(méi)有統(tǒng)一的標(biāo)準(zhǔn),以谷歌瀏覽器為例,他主要有四個(gè)進(jìn)程,分別是頂層的Browser process,負(fù)責(zé)標(biāo)簽頁(yè)網(wǎng)頁(yè)呈現(xiàn)的Renderer Process,負(fù)責(zé)控制網(wǎng)頁(yè)插件的Plugin Process和負(fù)責(zé)處理GPU的GPU Process。
每個(gè)進(jìn)程都可以通過(guò)創(chuàng)建線(xiàn)程或者調(diào)用線(xiàn)程來(lái)完成任務(wù)。打個(gè)比方,進(jìn)程就相當(dāng)于一個(gè) 公司,線(xiàn)程就相當(dāng)于公司的一個(gè)部門(mén),一個(gè)公司可以只有一個(gè)部門(mén)也可以有多個(gè)部分,公司內(nèi)部的資源是共享的。
二、瀏覽器的渲染進(jìn)程
我們前端主要和Renderer Process打交道,在這個(gè)進(jìn)程里面主要包含五個(gè)線(xiàn)程:
1、GUI渲染線(xiàn)程,用于解析html、css,構(gòu)建DOM樹(shù)和RenderObject樹(shù),布局和繪制等瀏覽器界面渲染任務(wù)|不能和JS引擎線(xiàn)程同時(shí)執(zhí)行。
2、js引擎線(xiàn)程,也就是我們常說(shuō)的js內(nèi)核,用于解析、運(yùn)行javascript代碼,不能和GUI渲染線(xiàn)程同時(shí)執(zhí)行。
3、事件觸發(fā)線(xiàn)程,用于監(jiān)聽(tīng)和觸發(fā)事件,可以理解成由js引擎線(xiàn)程來(lái)執(zhí)行事件綁定代碼,然后另開(kāi)事件觸發(fā)線(xiàn)程用來(lái)監(jiān)聽(tīng)事件的觸發(fā),觸發(fā)以后把事件處理函數(shù)里面的代碼交給js引擎線(xiàn)程來(lái)執(zhí)行,但是js是單線(xiàn)程的,事件處理函數(shù)里面的代碼會(huì)添加到待處理隊(duì)列的末尾,也就是等js線(xiàn)程空閑以后才會(huì)執(zhí)行。
4、定時(shí)觸發(fā)器線(xiàn)程,就是經(jīng)常使用的setInterval與setTimeout所使用的線(xiàn)程,可以理解成由js引擎線(xiàn)程來(lái)定義一個(gè)定時(shí)器,但是定時(shí)器的計(jì)數(shù)是另開(kāi)單獨(dú)線(xiàn)程來(lái)計(jì)時(shí)并觸發(fā)定時(shí),計(jì)時(shí)時(shí)間到了以后,把要執(zhí)行的回調(diào)函數(shù)里面的代碼添加到待處理隊(duì)列的末尾,等待js線(xiàn)程空閑以后執(zhí)行。
5、http請(qǐng)求線(xiàn)程,在我們使用XMLHttpRequest對(duì)象發(fā)送http請(qǐng)求的時(shí)候,是新開(kāi)一個(gè)線(xiàn)程請(qǐng)求,檢測(cè)到對(duì)象狀態(tài)改變時(shí),如果綁定了回調(diào)函數(shù),異步線(xiàn)程就產(chǎn)生狀態(tài)變更事件,將這個(gè)回調(diào)里面的代碼添加到j(luò)s線(xiàn)程末尾。
三、js引擎線(xiàn)程的單線(xiàn)程
1、分析下面代碼執(zhí)行過(guò)程,以定時(shí)器觸發(fā)線(xiàn)程為例看不同線(xiàn)程和js線(xiàn)程的執(zhí)行過(guò)程:
在上面代碼中,js代碼是由js引擎線(xiàn)程執(zhí)行的,這里要明確,js是單線(xiàn)程的,同一時(shí)間只能執(zhí)行一個(gè)的任務(wù),所以有多個(gè)等待任務(wù)的時(shí)候需要排隊(duì),排在前面的先執(zhí)行,排在后面的要等待前面任務(wù)完成才能執(zhí)行。開(kāi)始的t1時(shí)間里面,代碼至上而下執(zhí)行,當(dāng)碰到setTimeout,這是個(gè)定時(shí)器,需要計(jì)時(shí),把這個(gè)計(jì)時(shí)的任務(wù)交給定時(shí)觸發(fā)器線(xiàn)程(時(shí)間到了以后把要執(zhí)行的代碼排隊(duì)到t1的后面t2時(shí)間里面),繼續(xù)執(zhí)行js代碼。
2、分析下面代碼執(zhí)行過(guò)程,觀(guān)察js引擎線(xiàn)程和GUI線(xiàn)程的互斥:
在上面代碼中,開(kāi)始的t1時(shí)間里面,代碼至上而下執(zhí)行,當(dāng)碰到事件綁定的時(shí)候,把這個(gè)事件監(jiān)聽(tīng)的任務(wù)交給事件觸發(fā)線(xiàn)程,繼續(xù)執(zhí)行js代碼。當(dāng)用戶(hù)點(diǎn)擊按鈕,事件觸發(fā)線(xiàn)程監(jiān)聽(tīng)到事件,把事件處理函數(shù)里面的代碼從隊(duì)列末尾插入,在t2事件執(zhí)行里面有三行代碼第一行是box.innerHTML = "doing something",這行代碼是頁(yè)面渲染,交給GUI渲染線(xiàn)程,由于GUI渲染線(xiàn)程和js引擎線(xiàn)程是互斥的,這個(gè)時(shí)候先讓t2時(shí)間里面的其他js代碼wait(3000);繼續(xù)執(zhí)行,把box.innerHTML = "doing something"插入t2的后面t3時(shí)間里面,執(zhí)行完wait(3000)以后,最后一行是box.innerHTML = "finish",這行代碼也是頁(yè)面渲染,交給GUI渲染線(xiàn)程,由于GUI渲染線(xiàn)程和js引擎線(xiàn)程是互斥的,不能同時(shí)執(zhí)行,把box.innerHTML = "finish"從隊(duì)列末尾插入,變成t4時(shí)間執(zhí)行。所以,最后結(jié)果是,當(dāng)用戶(hù)點(diǎn)擊的時(shí)候,div里面沒(méi)有內(nèi)容,3秒以后直接顯示finish。
在前端學(xué)習(xí)過(guò)程中,js的單線(xiàn)程對(duì)大家理解同步和異步非常重要,熟悉js的單線(xiàn)程將讓大家能夠更好的理解事件,定時(shí)器和ajax請(qǐng)求的執(zhí)行過(guò)程。
最后如果你對(duì)web前端開(kāi)發(fā)感興趣的話(huà),不妨來(lái)千鋒大前端培訓(xùn)班了解一下我們的前端培訓(xùn)課程,全程名師面授,更有免費(fèi)學(xué)習(xí)資料可以領(lǐng)取,趕緊來(lái)了解一下吧。