一、為什么 JavaScript 單線程
假定JavaScript同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)DOM節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)?
為了避免復(fù)雜性, JS 采用了單線程的模式,也就是一次只能執(zhí)行一個(gè)程序
二、事件機(jī)制(觀察者模式)
其實(shí) JS 是一直有兩個(gè)線程在跑,只不過一個(gè)負(fù)責(zé)跑我們寫的主程序,另一個(gè)線程負(fù)責(zé)事件任務(wù)的監(jiān)聽并在需要響應(yīng)的時(shí)候發(fā)起通知。下面請看圖一:
由圖一我們很直觀的看出了 JS 分為了兩個(gè)線程,主線程中的主程序部分,就是非常規(guī)則的按照單線程的方式一行一行的去運(yùn)行我們編寫的非事件函數(shù)里的 JS 代碼。
而在主程序的黃色部分,則是碰到了需要注冊的事件代碼,圖中以 onclick 為例子,這時(shí)主程序就會在事件監(jiān)聽表里添加一條 onclick 的監(jiān)聽。這時(shí)候,次線程就開始去監(jiān)聽事件的行為了。當(dāng)次線程監(jiān)聽到這個(gè)事件所綁定的元素被點(diǎn)擊后,就會發(fā)出通知(在事件任務(wù)隊(duì)列里插入一條需要想要的事件)。之后次程序就一直做這個(gè)事。
主線程中,等到主程序執(zhí)行完畢后,系統(tǒng)就會讀取"任務(wù)隊(duì)列",如果發(fā)現(xiàn)有事件,則執(zhí)行。否則繼續(xù)讀取"任務(wù)隊(duì)列"直到下一個(gè)事件出現(xiàn)。
ps:圖中事件函數(shù)部分,綠色為等待事件任務(wù)出現(xiàn),紅色部分為正在執(zhí)行的事件函數(shù)。
主線程和次線程的流程:
主線程
主程序的執(zhí)行
系統(tǒng)就會讀取"任務(wù)隊(duì)列"
如果發(fā)現(xiàn)有事件出現(xiàn),則執(zhí)行,否則重復(fù)2
程序結(jié)束或者事件監(jiān)測注冊表為空的時(shí)候退出
ps:只要主線程空了,就會去讀取"任務(wù)隊(duì)列",這就是JavaScript的運(yùn)行機(jī)制。這個(gè)過程會不斷重復(fù)。
次線程
根據(jù)事件監(jiān)測注冊表的內(nèi)容進(jìn)行監(jiān)聽
發(fā)現(xiàn)事件監(jiān)測注冊表的內(nèi)容得到結(jié)果,則發(fā)起通知(在事件任務(wù)隊(duì)列里插入一條需要想要的事件)
重復(fù)1、2
二、為什么這樣的 JS 高效率
單線程就意味著,所有任務(wù)需要排隊(duì),前一個(gè)任務(wù)結(jié)束,才會執(zhí)行后一個(gè)任務(wù)。如果前一個(gè)任務(wù)耗時(shí)很長,后一個(gè)任務(wù)就不得不一直等著。
如果排隊(duì)是因?yàn)橛?jì)算量大,CPU忙不過來,倒也算了,但是很多時(shí)候CPU是閑著的,因?yàn)镮O設(shè)備(輸入輸出設(shè)備)很慢(比如Ajax操作從網(wǎng)絡(luò)讀取數(shù)據(jù)),不得不等著結(jié)果出來,再往下執(zhí)行。
JavaScript語言的設(shè)計(jì)者意識到