Asynchronous
的中文翻譯是非同步、異步,Synchronous
的中文翻譯是同步,以下都會直接用同步、非同步來介紹。
非同步程式設計是JavaScript
在學習時一個很大的重點,不光是容易誤解,不好理解,也因為實際上要常常使用到。
這一篇不講太多專業術語跟程式範例,會試著用很白話的方式把非同步這件事情給講清楚,那就直接開始進入今天的主題!
JavaScript
是單一執行緒這是我覺得在了解同步跟非同步之前要先理解的第一個點,先完全不要管什麼是同步,什麼是非同步,一開始請先知道這個:
Javascript程式執行,一次只能執行一件事情。
就像是一家餐廳,就只有一個廚師,那他就只能一次煮一道菜,假如有兩個廚師,就是同時煮兩道菜,很多廚師可以同時煮很多菜那就是多執行緒,可以一次同時進行很多事情。
無論如何,要記住這家叫做「Javascript」的餐廳,永遠只有一個廚師(執行緒)。
JavaScript
同步跟非同步剛開始學習時,同步會讓人感覺是,全部的動作同時開始進行,所以容易誤會(誤會同步=同時)。
後來去查閱一些資料後發現其實同步是 => 一次做一件事情。
既然同步是一次做一件事情,那非同步不就是一次做很多事情?
所以會發現到說很多教學文章會這樣寫:
同步:一次就只做一件事情
非同步:一次同時做很多件事情
使用"同時間"能處理事情的多寡來區分JS的同步跟非同步的文章真的很多。
可能這講法也沒有錯,但就會發生衝突,對剛學習沒多久的人造成心靈打擊,造成懂的人看得懂,不懂的人看不懂。
而我的理解 =>
單執行緒的JavaScript
就像是餐廳的只有一個廚師,假如要說非同步是一次同時做很多件事情,就好似說突然冒出一堆廚師一樣,最主要發生的衝突,就是上面說的:
Javascript程式執行,一次只能做一件事情。
也許可以用很多個廚師,同時做很多事情的方法來做到非同步,但我JavaScript
就只有一個廚師,怎麼辦呢?
先來看看另一個概念吧。
這個概念是我從執行資料平行處理的效能考量看到的,我覺得可以很好的解釋JavaScript
如何在自身只能是單執行緒的情況下,達成到非同步的效果,同時又不會跟單執行緒的特性(一次做一件事情)發生衝突。
以下節錄文章概念:
平行(Parallelism)與並行(Concurrency)不同,如果兩個任務分配到一個CPU核心,在取得的時間片段中交互執行,稱之為並行。如果有兩個核心,兩個任務各分配到其中之一同時執行,稱之為平行。現今開發者對於並行設計應不陌生,利用並行運算處理多個流程,讓客戶端以為任務「同時」執行,或者是某任務被阻斷(Block)之時,切換另一任務執行,避免等待而浪費CPU執行時間,用以提升整體任務執行效率。
很好的解釋了一些盲點區域,上面有提到很多教學會講說非同步是一次同時做很多事情,其實那就是一種平行處理,確實也是一種非同步,但卻永遠不可能是JavaScript
的非同步。
因為JavaScript
這間餐廳的廚師就只有一個,要有很多人才能平行耶,我畫示意圖比較清楚。
多人廚師 => 平行處理 => 同時做很多菜 => 大家都很開心:
只有一個廚師 => 平行處理 => 同時做很多菜 => 廚師不開心 :
那就一個個慢慢做如何?
客人不開心。
而且中間要是過程有一個卡住,之後的菜也就都不用做了。
欸,那該怎麼辦,我大JavaScript
餐廳就是只想請一個廚師,但又不想要廚師只能一個個做,那會花超久時間,聰明的大JavaScript
餐廳想到了一個好辦法,剛剛那個是平行處理,那何不來試試並行處理?
並行處理:
交互執行,稱之為並行,要怎麼辦到這件事情,簡單來說就是不卡住,那是什麼意思?比如來說小白今天要做兩件事情,一個是烤土司一個是倒牛奶,而小白今天就只有一個人,所以沒辦法使用平行處理,除非小白會影分身。
除了慢慢來,先烤完吐司再倒牛奶之外,還有另外一種選擇,你可以先把你的吐司塞到烤土司機之後,不要在那邊傻傻等它,按下按鈕開始烤後,就衝去冰箱拿牛奶出來倒,倒完再看吐司好了沒。
這種做完後不停留到完全結束,就直接去做下一步的處理方式其實就是並行處理。
也因為這樣,所以看起來結果會很像是同時開始做,但是並沒有,可以來比較差異時間,請小白為我們示範一下,示意圖有請:
為什麼有一堆人會覺得JS非同步就是「同時」做很多事情,我覺得關鍵這就是在這邊,但因為單執行緒的觀念,就勢必不可能同時(平行處理),所以JS所謂的「非同步」操作,其實是比較偏向示意圖的第三種,也就是並行處理,這也是我個人所理解的Asynchronous Programming (非同步程式設計)。
根據上面的想法,我認為同步在處理事件的流程會被「卡住」,卡住的意思是說,當遇到沒辦法馬上執行完成的東西,就會整個停住,下一件要做的事情就會停擺。
而非同步在處理事件的流程不會被「卡住」,所以因為不會被卡住,所以看起來像是「一次同時做很多件事情」,但實際上沒有。
而這卡住不卡住其實比較跟阻塞(blocking)與非阻塞(non-blocking)有關,詳細的可以去看Node.js的官方文件有講到這一塊:Overview of Blocking vs Non-Blocking
這邊只簡單介紹:
很像剛剛在描述同步跟非同步吧?其實在JavaScript
當中,可以把兩個劃上等號,剛剛Node.js的官方文件有一段是這樣講的:
圖片來源:Node.js官方文件
這段的意思就是,阻塞的方法會同步的方式執行,而非阻塞的方法會非同步的方式執行,所以我會把兩種當作是同一種東西,注意僅限於JavaScript
,其他語言可能不一定,要是全部都一模一樣那幹嘛分兩個不一樣的名稱對吧?
準備要來下結論了,前面有提過到的一次做很多事情,看完上面的文章,相信你已經知道那並不是JavaScript
非同步的特色,JavaScript
永遠不會一次做很多事情,而實際上是怎麼在JavaScript
做到非同步這件事情的,這就是明天要來講的主題,我們明天見啦~
[1] MDN - Introducing asynchronous JavaScript
[2] W3Schools - Asynchronous JavaScript
[3] You Don't Know JS - Asynchrony
[4] Javascript 非同步 & Event Loop!10 分鐘輕鬆圖解學習!
[5] 你懂 JavaScript 嗎?#22 非同步:現在和以後
[6] 重新認識 JavaScript: Day 26 同步與非同步
[7] 所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU
[8] JavaScript 中的同步與非同步(上):先成為 callback 大師吧!
[9] 異步程式設計與事件迴圈