javascript中非同步單線程的解析(圖文)

來源:互聯網
上載者:User
本篇文章給大家帶來的內容是關於JS中非同步單線程的解析(圖文),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所協助。

對於通常的developer(特別是那些具備並行計算/多線程背景知識的developer)來講,js的非同步處理著實稱得上詭異。而這個詭異從結果上講,是由js的“單線程”這個特性所導致的。

我曾嘗試用“先定義後展開”的教科書方式去講解這一塊的內容,但發現極其痛苦。因為要理清楚這個東西背後的細節,並將其泛化、以更高的視角來看問題,著實涉及非常多的基礎知識。等到我把這些知識講清楚、講完,無異於逼迫讀者抱著作業系統、電腦網路這樣的催眠書看上好個幾章節,著實沉悶而乏味。

並且更關鍵的是,在走到那一步的時候,讀者的精力早已消耗殆盡,完全沒有心力再去關心這個最開始的問題——js的非同步處理為何詭異。

所以,我決定反過來,讓我們像一個初學者那樣,從一無所知開始,

先使用“錯誤的理念”去開始我們的討論,然後用代碼去發現和理念相違背的地方。

再做出一些修正,再考察一些例子,想想是否還有不大滿意和清楚的地方,再調整。如此往複,我們會像偵探那樣,先從一個不大正確的假設開始,不斷尋找證據,不斷修正假設,一步步追尋下去,直到抵達最後完整的真相。

我想,這樣的寫作方式,更符合一個人真正的求知和研究過程,並能夠為你帶來更多關於“探索問題”的啟發。我想,這樣的思維方式和研究理念,比普通的知識更為重要。它能夠讓你成為知識的獵人,有能力獨立地覓食,而不必被迫成為嬰孩,只能坐等他人餵食。

好了,讓我們先從一塊js代碼,開始我們的探索之旅。

console.log('No. 1');setTimeout(function(){    console.log('setTimeout callback');}, 5000);console.log('No. 2');

輸出結果是:

No. 1No. 2setTimeout callback

這塊代碼中幾乎沒什麼複雜的東西,全是列印語句。唯一的特別是函數setTimeout,根據粗略的網上資料顯示,它接受兩個參數:

  • 第一個參數是callback函數,就是讓它執行完之後,回過頭來調用的函數。

  • 另一個是時間參數,用於指定多少微妙之後,執行callback函數。這裡我們使用了5000微妙,也即是5秒鐘。

另一個重點是,setTimeout是一個非同步函數,意思是我的主程式不必去等待setTimeout執行完畢,將它的運行過程扔到別的地方執行,然後主程式繼續往下走。也即是,主程式是一個步調、setTimeout是另一個步調,即是“非同步”的方式跑代碼。

如果你有一些並行計算或者多線程編程的背景知識,那麼上面的語句就再熟悉不過了。如果在多線程環境,無非是另起一根線程去運行列印語句console.log('setTimeout callback')。然後主線程繼續往下走,新線程去負責列印語句,清晰明了。

所以綜合起來,這段代碼的意思是,主線程執行到語句setTimeout時,就把它交給“其它地方”,讓這個“其它地方”等待5秒鐘之後運行。而主線程繼續往下走,去執行“No. 2”的列印。所以,由於其它部分要等待5秒鐘之後才運行,而主線程立刻往下運行了“No. 2”的列印,最終的輸出結果才會是先列印“No. 2”,再列印“setTimeout callback”。

嗯,so far so good。一切看來都比較美好。

如果我們對上述程式做一點變動呢?例如,我可不可以讓“setTimeout callback”這個資訊先被列印出來呢?因為在並行計算中,我們經常遇到的問題便是,由於你不知道多個線程之間誰執行得快、誰執行得慢,所以我們無法判定最終的語句執行順序。這裡我們讓“setTimeout callback”停留了5秒鐘,時間太長了,要不短一點?

console.log('No. 1');setTimeout(function(){    console.log('setTimeout callback');}, 1);console.log('No. 2');

我們將傳遞給setTimeout的參數改成了1毫秒。多次運行後會發現,結果竟然沒有改變?!似乎有點反常,要不再改小一點?改成0?

console.log('No. 1');setTimeout(function(){    console.log('setTimeout callback');}, 0);console.log('No. 2');

多次運行後,發現依舊無法改變。這其實是有點奇怪了。因為通常的並行計算、多線程編程中,通過多次運行,你其實是可以看到各種無法預期的結果的。在這裡,竟然神奇地得到了相同的執行順序結果。這就反常了。

但我們還無法完全下一個肯定的結論,可不可能因為是setTimeout的啟動時間太長,而導致“No. 2”這條語句先被執行呢?為了做進一步的驗證,我們可以在“No. 2”這條列印語句之前,加上一個for迴圈,給setTimeout充分的時間去啟動。

console.log('No. 1');setTimeout(function(){    console.log('setTimeout callback');}, 0);for (let i = 0; i < 10e8; i++) {}console.log('No. 2');

運行這段代碼,我們發現,"No. 1"這條列印語句很快地顯示到了瀏覽器命令列,等了一秒鐘左右,接著輸出了

No. 2setTimeout callback

誒?!這不就更加奇怪了嗎?!setTimeout不是等待0秒鐘後立刻運行嗎,就算啟動再慢,也不至於等待一秒鐘之後,還是無法正常顯示吧?況且,在加入這個for迴圈之前,“setTimeout callback”這條輸出不是立刻就顯示了嗎?

綜合這些現象,我們有理由懷疑,似乎“setTimeout callback”一定是在“No. 2”後顯示的,也即是:setTimeout的callback函數,一定是在console.log('No. 2')之後執行的。為了驗證它,我們可以做一個危險一點的測試,將這個for迴圈,更改為無限while迴圈。

console.log('No. 1');setTimeout(function(){    console.log('setTimeout callback');}, 0);while {}  // dangerouse testingconsole.log('No. 2');

如果setTimeout的callback函數是按照自己的步調做的運行,那麼它就有可能在某個時刻列印出“setTimeout callback”。而如果真的是按照我們猜測的那樣,“setTimeout callback”必須排在“No. 2”之後,那麼瀏覽器命令列就永遠不會出現“setTimeout callback”。

運行後發現,在瀏覽器近乎要臨近崩潰、達到記憶體溢出的情形下,“setTimeout callback”依舊沒有列印出來。這也就證明了我們的猜測!

這裡,我們第一次出現了理念和現實的矛盾。按照通常並行計算的理念,被扔到“其它地方”的setTimeout callback函數,應該被同時運行。可事實卻是,這個“其它地方”並沒有和後一條列印“No. 2”的語句共同執行。這時候,我們就必須要回到基礎,回到js這門語言底層的實現方式上去追查,以此來挖掘清楚這後面的陷阱。

js的特性之一是“單線程”,也即是從頭到尾,js都在同一根線程下運行。或許這是一個值得調查深入的點。想來,如果是多線程,那麼setTimeout也就該按照我們原有的理念做執行了,但事實卻不是。而這兩者的不同,便在於單線程和多線程上。

找到了這個不同點,我們就可以更深入去思考一些細節。細想起來,所謂“非同步”,就是要開闢某個“別的地方”,讓“別的地方”和你的主運行路線一起運行。可是,如果現在是單線程,也就意味著計算資源有且只有一份,請問,你如何做到“同時運行”呢?

這就好比是,如果你去某個辦事大廳,去繳納水費、電費、天然氣。那麼,我們可以粗略地將它們分為水費櫃檯、電費櫃檯、天然氣櫃檯。那麼,如果我們依次地“先在水費櫃檯辦理業務,等到水費的明細列印完畢、繳納完費用後;再跑去電費櫃檯列印明細、加納費用;再跑去天然氣櫃檯列印明細、加納費用”,這就是一個同步過程,必須等待上一個步驟做完後,才能做下一步。

而非同步呢,就是說我們不必在某個環節浪費時間瞎等待。比如,我們可以在“列印水費明細”的空閑時間,跑到電費和天然氣櫃檯去辦理業務,將“電費明細、天然氣明細的列印”這兩個任務提前啟動起來。再回過頭去繳納水費、繳納電費、繳納天然氣費用。其實,這就是華羅庚推廣優選法的時候舉的例子,燒水、倒茶葉、泡茶,如何安排他們的順序為高效。

顯然,非同步地去做任務更高效。但這要有一個前提,就是你做任務的資源,也即是幹活的人或者機器,得有多份才行。同樣按照上面的例子來展開討論,雖然有水費、電費、天然氣這三個櫃檯,可如果這三個櫃檯背後的辦事人員其實只有一個呢?比如你啟動了辦理水費的業務,然後想要在辦理水費業務的等待期,去電費櫃檯辦理電費業務。表面上,你去電費櫃檯下了申請單,請求辦理電費業務,可卻發現根本沒有辦事員去接收你的這個業務!為何?因為這有且只有一個的辦事員,還正在辦理你的水費業務啊!這時候,你的這個所謂的“非同步”,有何意義?!

所以從這個角度來看,當計算資源只有一份的時候,你做“非同步”其實是沒什麼意義的。因為幹活的資源只有一份,就算在表面做了名義上的“非同步”,可最終就像上面的多櫃檯單一辦事員那樣,到了執行任務層面,還是會一個接一個地完成任務,這就沒有意義了。

那麼,js的特性是”單線程“+”非同步“,不就正是我們討論的“沒有意義”的情況嗎?!那又為何要多次一舉,幹一些沒有意義的事情呢?

嗯......事情變得越來越有趣了。

通常來講,如果一個事件出現了神奇和怪異的地方,基本上都是因為我們忽略了某個細節,或者對某個細節存在誤解或是錯誤理解。要想把問題解決,我們就必須不斷地回顧已有材料,在不斷地重複檢驗中,發現那幾根我們忽略的陷阱。

讓我們回顧一下關於js非同步宣傳片。通常為了說明js非同步必要性,會舉出瀏覽器的資源載入和頁面渲染這個矛盾。

渲染,可以比較粗糙地理解為將“畫面”畫出來的過程。例如,瀏覽器要將頁面上的按鈕、圖片顯示出來,就必須有一個將“圖片”在網頁上畫出來的動作。又或是,作業系統要將“案頭”這個圖形介面顯示在顯示器上,就必須要把它相應的這個“畫面”在顯示器上畫出來的動作。歸結起來,這個“畫出來”的過程,就被稱之為“渲染”。

例如,你點擊頁面上的一個button,讓瀏覽器去後端資料庫將資料報表取出來,在網頁上把數字顯示出來。而如果js不支援非同步話,整個網頁的就會停留,也即是“卡”,在滑鼠點擊按鈕這一個動作上,頁面無法完成後續的渲染工作。一直要等到後端把資料返回到了前端,程式流才能夠繼續跑下去。

所以這裡,js的“非同步”其實是為了讓瀏覽器將“載入”這個任務分給“其它地方”,讓“載入過程”和“渲染過程”同步進行下去。

等等,又是這個“其它地方”?!!

我擦,不是說js是單線程而麼,計算資源不是只有一份麼,怎麼又可以“一邊載入、一邊渲染”了?!WTF,你這是在逗我玩兒嗎?!

艸,到底這裡面哪句話是真的?!到底js是單線程是真的?還是說瀏覽器可以同時做“一邊載入、一邊渲染”這個事情是真的?!

如何才能解決這個疑惑?!很顯然,我們必須要深入到瀏覽器的內部,去看一看它到底是怎麼樣被設計的。

在搜尋引擎中,做一些關於瀏覽器和js的搜尋,我們不難得到一些基本資料。js並不是瀏覽器的全部,瀏覽器要掌管的事情太多了,掌管js的只是瀏覽器的一個組件,叫做js引擎。而最出名的、並在Chrome中使用的,就是大名鼎鼎的V8引擎,它負責js的解析和運行。

另一方面我們還知道,使用js的一個很大原因,是因為它能夠自由地去操控DOM元素、去執行Ajax非同步請求、能夠像我們最開始舉的例子那樣,使用setTimeout做非同步任務分配。這些都是js優秀特性。

可令人驚訝的事情來了,當我們去探索這個掌管js一切的V8引擎的時候,我們卻發現,它並不提供DOM的操控、Ajax的執行以及setTimeout的特性:

來自Alexander Zlatkov,它的結構是:

  1. JS引擎

    • Memory Heap

    • Call Stack

  2. Web APIs

    • DOM (Document)

    • Ajax (XMLHttpRequest)

    • Timeout (setTimeout)

  3. Callback Queue

  4. Event Loop

明明是js的特性,為什麼這些職能卻不是由js的引擎來掌管呢?嗯,interesting~~~

誒!不是“單線程”麼,不是載入過程被扔到其它地方嗎?!js是單線程,也即是js在js引擎中是單線程、只能夠分到一份計算資源,可是,載入資料的Ajax這個feature不是沒有被放到js引擎嗎?!

艸!真TM是老狐狸啊!還以為“單線程”和“一邊載入、一邊渲染”這兩種說法只有一種是對的,可結果是,都對!為什麼呢?因為只說了js是單線程,可沒說瀏覽器本身是單線程啊!所以咯,渲染相關的js部分可以和資料載入的Ajax部分是可以同時進行的,因為它們根本就在兩個模組,即兩個線程嘛!所以當然可以並行啊!WTF!

誒~等等,讓我們再仔細看看上面這張圖呢?!Ajax不在js引擎裡,可是setTimeout也不在js引擎裡面啊!!如果Web APIs這部分是在不同於js引擎的另外一根線程裡,它們不就可以實現真正意義上的並行嗎?!那為何我們開頭的列印資訊“setTimeout callback”,無法按照並行的方式,優先於“No. 2”列印出來呢?

嗯......真是interesting......事情果然沒有那麼簡單。

顯然,我們需要考察更多的細節,特別是,每一條語句在中,是按照什麼順序被移動、被執行的。

談到語句的執行順序,我們需要再一次將關注點放回到js引擎上。再次回看上面這幅結構圖,JS引擎包含了兩部分:一個是 memory heap,另一個是call stack。前者關於記憶體配置,我們可以暫時放下。後面即是函數棧,嗯,它就是要進一步理解執行順序的東西。

函數棧(call stack)為什麼要叫做“棧(stack)”呢?為什麼不是叫做函數隊列或者別的神馬?這其實可以從函數的執行順序上做一個推斷。

函數最開始被引進,其實就是為了代碼複用和模組化。我們期望一段本該出現的代碼,被單獨提出來,然後只需要用一個函數調用,就可以將這段代碼的執行內容給插入進來。

所以,如果當我們執行一段代碼時,如果遇到了函數調用,我們會期望先去將函數裡面的內容執行了,再跳出來回到主程式流,繼續往下執行。

所以,如果把一個函數看作函數節點的話,整個執行流程其實是關於函數節點的“深度優先”遍曆,也即是從主函數開始啟動並執行函數調用,整個呈深度優先遍曆的方式做調用。而結合演算法和資料結構的知識,我們知道,要實現“深度遍曆”,要麼使用遞迴、要麼使用stack這種資料結構。而後者,無疑更為經濟使用。

所以咯,既然期望函數調用呈深度優先遍曆,而深度優先遍曆又需要stack這種資料結構做支援,所以維護這個函數調用的結構就當然呈現為stack的形式。所以叫做函數棧(stack)。

當然,如果再發散思考一下,作業系統的底層湧來維護函數調用的部分也叫做函數棧。那為何不用遞迴的方式來實現維護呢?其實很簡單,電腦這麼個啥都不懂的東西,如何知道遞迴和返回?它只不過會一往無前的一直執行命令而已。所以,在沒有任何輔助結構的情況下,能夠一往無前地執行的方式,只能是stack,而不是更為複雜的遞迴概念的實現。

另一方面,回到我們最開頭的問題,矛盾其實是出現在setTimeout的callback函數上。而上面的結構圖裡,還有一部分叫做“callback queue”。顯然,這一部分也是我們需要瞭解的東西。

結合js的call stack和callback queue這兩個關鍵詞,我們不難搜尋到一些資料,來展開討論這兩部分是如何同具體的語句執行相結合的。

先在整體上論述一下這個過程:

  • 正常的語句執行,會一條接一條地壓入call stack,執行,再根據執行的內容繼續壓入stack。

  • 而如果遇到有Web APIs相關的語句,則會將相應的執行內容扔到Web APIs那邊。

  • Web APIs這邊,可以獨立於js引擎,並行地分配給它的語句,如Ajax資料載入、setTimeout的內容。

  • Web APIs這邊的callback function,會在在執行完相關語句後,被扔進“callback queue”。

  • Event loop會不斷地監測“call stack”和“callback queue”。當“call stack”為空白的時候,event loop會將“callback queue”裡的語句壓入到stack中,繼續做執行。

  • 如此迴圈往複。

以上內容比較抽象,讓我們用一個具體的例子來說明。這個例子同樣來自於Alexander Zlatkov。使用它的原因很簡單,因為Zlatkov在blog中使用的說明圖,實在是相當清晰明了。而目前我沒有多餘的時間去使用PS繪製相應的結構圖,就直接拿來當作例子說明了。

讓我們考察下面的程式碼片段:

console.log('Hi');setTimeout(function cb1() {     console.log('cb1');}, 5000);console.log('Bye');

哈哈,其實和我們使用的代碼差不多,只是列印的內容不同。此時,在運行之前,整個底層的結構是這樣的:

然後,讓我們執行第一條語句console.log('Hi'),也即是將它壓入到call stack中:

然後js引擎執行stack中最上層的這條語句。相應的,瀏覽器的控制台就會列印出資訊“Hi”:

由於這條語句被執行了,所以它也從stack中消失:

再來壓入第二條語句setTimeout

執行setTimeout(function cb1() { console.log('cb1'); }, 5000);

注意,由於setTimout部分並沒有被包含在js引擎中,所以它就直接被扔給了Web APIs的Timeout部分。這裡,stack中的藍色部分起到的作用,就是將相應的內容“timer、等候時間5秒、回呼函數cb1”扔給Web APIs。然後這條語句就可以從stack中消失了:

繼續壓入下一條語句console.log('Bye')

注意,此時在Web APIs的部分,正在並行於js引擎執行相應的語句,即:等候5秒鐘。Okay,timer繼續它的等待,而stack這邊已經有語句了,所以需要把它執行掉:

相應的瀏覽器控制台,就會顯示出“Bye”的資訊。而stack中運行後的語句,就該消失:

此時,stack已經為空白。Event loop檢測到stack為空白,自然就想要將callback queue中的語句壓入到stack中。可此時,callback queue中也為空白,於是Event loop只好繼續迴圈檢測。

另一方面,Web APIs這邊的timer,並行地在5秒鐘後開始了它的執行——什麼也不做。然後,將它相應的回呼函數cb1(),放到callback queue中:

Event loop由於一直在迴圈檢測,此時,看到callback queue有了東西,就迅速將它從callback queue中取出,然後將其壓入到stack裡:

現在Stack裡有了東西,就需要執行回呼函數cb1()。而cb1()裡面調用了 console.log('cb1')這條語句,所以需要將它壓入stack中:

stack繼續執行,現在它的最上層是 console.log('cb1'),所以需要先執行它。於是瀏覽器的控制它列印出相應的資訊“cb1”:

將執行了的 console.log('cb1')語句彈出棧:

繼續執行cb1()剩下的語句。此時,cb1()已經沒有其它需要執行的語句了,也即是它被運行完畢,所以,將它也從stack中彈出:

整個過程結束!如果從頭到尾看一遍的話,就是下面這個gif圖了:

相當清晰直觀,對吧!

如果你想進一步地把玩js的語句和call stack、callback queue的關係,推薦Philip Roberts的一個GitHub的開源項目:Loupe,裡面有他online版本供你做多種嘗試。

有了這些知識,現在我們回過頭去看開頭的那段讓人產生疑惑的代碼:

console.log('No. 1');setTimeout(function(){    console.log('setTimeout callback');}, 0);console.log('No. 2');

按照上面的js處理語句的順序,第一條語句console.log('No. 1')會被壓入stack中,然後被執行的是setTimout

根據我們上面的知識,它會被立刻扔進Web APIs中。可是,由於這個時候我們給它的等待時間是0,所以,它的callback函數console.log('setTimeout callback')會立刻被扔進“Callback Queue”裡面。所以,那個傳說中的“其它地方”指的就是callback queue。

那麼,我們能夠期望這一條console.log('setTimeout callback')先於“No. 2”被列印出來嗎?

其實是不可能的!為什嗎?因為要讓它被執行,首先它需要被壓入到call stack中。可是,此時call stack還沒有將程式的主分支上的語句執行完畢,即還有console.log('No. 2')這條語句。所以,event loop在stack還未為空白的情況下,是不可能把callback queue的語句壓入stack的。所以,最後一條“setTimeout callback”的資訊,一定是會排在“No. 2”這條資訊後面被列印出來的!

這完全符合我們之前加入無限while迴圈的結果。因為主分支一直被while迴圈佔有,所以stack就一直不為空白,進而,callback queue裡的列印“setTimeout callback”的語句就更不可能被壓入stack中被執行。

探索到這裡,似乎該解決的問題也都解決了,好像就可以萬事大吉,直接封筆走人了。可事實卻是,這才是我們真正的泛化討論的開始!

做研究和探索,如果停留於此,就無異於小時候自己交作業給老師,目的僅僅是完成老師布置的任務。在這裡,這個老師布置的任務就是文章開頭所提出的讓人疑惑的代碼。可是,解決這段代碼並不是我們的終極目的。我們需要泛化我們的所學和所知,從更深層次的角度去探索,為什麼我們會疑惑,為什麼一開始無法發現這些潛藏在表面之下不同。我們要繼續去挖掘,我們到底在哪些最根本的問題上出現了誤解和錯誤認識,從而導致我們一路如此辛苦,無法在開頭看到事情的真相。

回顧我們的曆程,一開始讓我們載跟鬥的,其實就是對“非同步”和“多線程”的固定假設。多線程了,就是非同步,而非同步了,一定是多線程嗎?我們潛意識裡是很想做肯定回答的。這是因為如果非同步了,但卻是單線程,整個非同步就沒有意義了(回憶那個多櫃檯、單一辦事員的例子)。可js卻巧妙地運用了:使用非同步單線程去分配任務,而讓真正做資料載入的Ajax、或者時間等待的setTimeout的工作,扔給瀏覽器的其它線程去做。所以,本質上js雖然是單線程的,可在做實際工作的時候,卻利用了瀏覽器自身的多線程。這就好比是,雖然是多櫃檯、單一辦事員,可辦事員將繳納電費、水費的任務,外包給其它公司去做,這樣,雖然自己仍然是一個辦事員,但卻由於有了外包服務的支援,依舊可以一起並行來做。

另一方面,js的非同步、單線程的特性,逼迫我們去把並行計算中的“同步/非同步、阻塞/非阻塞”等概念理得更清楚。

“同步”的英文是synchronize,但在中文的語境下,卻很容易和“同時”掛鈎。於是,在潛意識裡有可能會有這樣一種聯想,“同步”就是“同時”,所以,一個同步(synchronize)的任務就被理解為“可以一邊做A,一邊做B”。而這個潛意識的印象,其實完全是錯誤的(一般做A一邊做B,其實是“非同步”+“並行”的情況)。

但在各類百科詞典上,確實有用“同時”來作為對“同步”的解釋。這是為什麼呢?其實這是對”同步“用作”同時“的一個混淆理解。如果仔細考慮”同時“的意思,細分起來,其實是有兩種理解:

  • 同一個時刻(at the same time),例如在9:00 a.m這個時間點,我們既在做A也在做B。

  • 另一個是同一個時間參考系,也就是所謂的clock on the wall是同一個。

前者很容易理解,這裡我重點解釋一下後者。例如,我在中國大陸同美國的一個同學開語音交談,我這邊是22:00,他那邊是9:00。我們做聊天這件事情的時候,是同一時刻(at the same time),但卻不在同一個時間參考體系(clock on the wall)。而在電腦中討論的同步,其實討論的是後者的”同一參考系“,同步,就是讓我們的參考系統一起來,放到同一個體系之下。

又比如,我們在生活中很容易說,同步你的電腦、同步你的手機通訊錄、同步你的相簿,說的是什麼呢?就是讓你的各個用戶端:PC、手機,同server端伺服器的內容都保持一致,也即是大家都被放到一個一致的參考系裡面。不要說,你在PC裡有照片A,而在手機裡沒有A卻有B,這個時候,談論PC裡資訊人與談論手機裡資訊的人,就是在雞同鴨講。究其原因,就是沒有把大家放到同一個參考系裡面。

所以,同步synchronize所指的”同時“,是大家把牆上的時鐘都調整到一致、調整為同一個步調,也即是同時、同一時間參考系的意思。而不是說,讓事情在同一時刻並列發生。自然的,什麼是非同步(asynchronize)呢,非同步就是大家的時間參考系是不同的,例如我在中國大陸、你在美國,我們的時間參考體系是不同的,這就是非同步,不在同一個步調、頻段上。

事實上,每一個獨立的人、每一塊獨立的計算資源,它都代表了一個各自的參考體系。只要你將任務分發給了其他人或是其它計算資源,此時,就出現了兩個參考體系:一個是原有主分支的參考體系,另一個是新的計算資源的參考體系。在並行計算中,有一個同步機制是使用語句barrier,目的是讓所有的計算分支在這一個位置節點都完成了計算。為什麼說它是一種同步機制?按照我們統一參考體系的理解,就是保證其他所有計算分支完成計算,也就保證了其它分支的消失,從而只剩下主分支這一個參考體系。於是大家可以談論同樣的東西,說同樣的話,不會有誤解。

另一方面,如果要更深入地理解js的設計,我認為還需要回到電腦曆史的初期,例如那個只有單核的分時系統的時代。在那樣一個時代,作業系統所受到的硬體上的限制,不亞於js引擎在瀏覽器中所受到的限制。在同樣的限制下,曾經的作業系統會如何去巧妙運用那極為有限的計算資源,讓整個作業系統給人以平滑、順暢和功能強大的錯覺?我想,js的這些設計必定和作業系統早期的設計緊密相關。所以在這個層面上,它將再一次回到作業系統這樣的基礎知識上。能否吃透現代的技術,其實很大層面上取決於你是否吃透了設計的曆史,是否理解在那些資源枯竭的年代,各路大神是如何巧妙地逢山開路,遇水搭橋。無論現代的電腦硬體資源有多麼豐富,它必定會因為目標的主次關係、業務的主次關係受到限制。而如何在限制中跳舞和創造,這是能夠貫穿整個曆史的共同性問題。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.