1. 程式人生 > >javascript既然是單執行緒語言 , 為什麼會分主執行緒和訊息執行緒(event loop) ?

javascript既然是單執行緒語言 , 為什麼會分主執行緒和訊息執行緒(event loop) ?

縱觀那近萬字的作文,只有這麼一段是直接回答題主問題的,但是回答還是有問題的!如果再幾年前,這麼說還行,但是現在,不行! 首先在瀏覽器端,我們的js程式碼已經可能不全是在一個執行緒中了,這是由於h5引入了web workers,詳請請移步 使用 Web Workers。它允許我們開一個工作執行緒處理一些耗時的任務,工作執行緒通過postmessage向js ui執行緒(這個概念我們下文在說,在此可以簡單理解為javascript的主執行緒)傳送訊息來進行執行緒間通訊。 而我們在js 主執行緒中要通過監聽web worker的message訊息來獲得工作執行緒中的資料,這裡的關鍵在工作執行緒和主執行緒之見的通訊,實際上,瀏覽器是通過一個訊息佇列來實現,工作執行緒發出資料時,通過事件event,向js主執行緒發一個通知,然後將資料插入到訊息佇列中,這樣, js在主執行緒就能收到通知,並從訊息佇列中去除資料(這是一個基本的過程,具體實現視瀏覽器而定),瞭解了這一點,你現在在去看看web workers的api,是不是好理解了許多。也許到這裡,你已然決定去取消之前點的讚了,但是彆著急,送佛送到西。這只是反駁了igetit所說的javascript只有一個執行緒。或許有人會說ajax也是系統單獨開了一個執行緒去執行的網路請求,那麼h5沒有引入web worker之前是不是也是說javascript也有多個縣城呢? 錯! 在沒有引入web worker之前,javascript確確實實是執行在一個單執行緒裡面!那ajax 怎麼說? 回憶一下呼叫ajax的過程,我們是需要把成功回撥傳遞給xhr,典型的程式碼如下:
 xhr = new XMLHttpRequest();
 xhr.onreadystatechange=function(){} //傳入我們的回撥
 xhr.open(...)
 xhr.send(...)

瀏覽器雖然會在一個單獨的執行緒去進行網路請求,但是我們是通過傳遞一個回撥的方式去處理資料,瀏覽器在網路請求成功後,然後會切換回js執行緒來執行我們的回撥,也就是說我們所有的js程式碼都是在js執行緒中執行的。所以javascript確實是在一個單執行緒中,而web worker不同,我們的js程式碼可以執行在js主執行緒之外,這也是為什麼不能在web worker裡面直接共享js主執行緒中定義的變數,不能操作ui (dom樹)的原因,因為根本不在一個執行緒!其實說的這裡,可以繼續往深的說,比如為什麼不能在工作執行緒中操作ui? 太深了就跑題了,如果有人感興趣,可以私我。我們稍微再說一些,比如igetit同學文章中提到了作業系統的程序和執行緒,既然你們都點過贊,那就證明已經看到了,可能有人會說,程序的地址空間是隔離的,但同一個程序的執行緒之見是可以共享資料的,為什麼web worker和js 雖然不在同一執行緒,但是如果在同一個程序中,也是應該能共享變數的,為甚麼web worker和js 主執行緒之見藥通過訊息對列這種複雜的方式來傳遞資料?為什麼不允許在web worker中操作UI? 如果有這些問題問的很好,但是我並不打算在此問題中回答,畢竟和題主問題不大,還是那句話,如果有興趣可以私我。
下面再看一個igetit同學對瀏覽器為什麼設計成單執行緒,和JavaScript可否設計成多執行緒?的回答:說最初是因為硬體配置低,所以沒有考慮多執行緒,後來有機會但是,好在js支援非同步,也就沒有實現。
我敢保證igetit同學沒有做過系統原生的ui開發!無論是windows c開發,活著java 介面開發,一條黃金原則就是不要在其它執行緒中操作ui,android中如果發現在非ui執行緒中操作ui會直接丟擲異常!好,繼續說,其實阮一峰這篇文章中對

為什麼JavaScript是單執行緒這個問題的回答是對的,也是最主要的原因傳送門JavaScript 執行機制詳解:再談Event Loop。 執行緒之間同步是有開銷的,並且面臨著同步問題,如果所有的執行緒都能操作ui,一旦cpu發生執行緒切換,都會面臨資料不完整的風險,例如a執行緒ui介面改了一半,cpu發生執行緒切換,b執行緒又去改同一處。而介面本質上來說只是作業系統對資料在顯示器上的一個對映,各個執行緒操作的都是資料,這麼一來,你改我也改,你還沒改完我有改,你改了一半我接著改,那還怎麼玩,所以要支援多執行緒,必須提供同步工具(然執行緒之間不會彼此發生衝突)。而如果在js中支援多執行緒,不僅會增加js虛擬機器的複雜度,也會增加編碼的複雜度(程式猿不得不自己處理好同步問題),所以,這才是js 為什麼到現在還是一個主執行緒的本質原因(先忽略web worker)。

現在我們回到題主的問題,zeromike 同學的回答其實已經說明了問題,但大家贊卻點錯了地方。 正如ajax回撥一樣,js vm會將我們所有的回撥都會放在一個隊列當中,比如我們監聽的某個單擊事件的回撥,當用戶單擊了我們監聽的元素,瀏覽器捕獲到事件,然後就去執行我們的回撥,而執行回撥的環境都在同一個javasricpt執行緒中,其實也就是說event loop是在瀏覽器中的,而javascript是執行在同一個執行緒當中的!這也是js的特點-非同步,node中也是延續了這個特點,當然,為了利用多核cpu,node 提供了child_process 。但這不是嚴格意義上的多執行緒,相當於起了多個node例項,也就有多個js執行緒。