ASP.NET MVC Framework - Sync & Async
Sync & Async
同步 Sync 與非同步 Async
- Sync 同步
- 發起 I/O 請求的執行緒不從正在呼叫的 I/O 操作函式返回(被阻塞),直到 I/O 操作完成
- Async 非同步
- 發起 I/O 請求的執行緒不等 I/O 操作完成,就繼續執行隨後的程式,而 I/O 的結果再透過其他方式通知發起 I/O 請求的程式
簡單來說!
同步 Sync:
我有好多事要做,但一次只能做一件事,我要等這件事做完才能做其他
非同步 Async:
我有好多事要做,但有些事開始做以後要等他完成,我可以在這期間去做其他事,直到他做好後再回去處理他
Sync vs Async
已準備早餐為例,要吃的東西有:
- 咖啡
- 蛋
- 培根
- 吐司
- 柳橙汁
Sync
以同步 Sync 的例子來說,我們會這樣準備:
圖片來源
- 倒杯咖啡
- 加熱鍋子,發呆等著鍋子加熱
- 熱好鍋子後煎兩顆蛋,發呆等著蛋煎熟
- 煎完蛋後煎三片培根,發呆等著培根煎熟
- 把吐司放進吐司機加熱,發呆等著吐司熱
- 等吐司加熱好後,抹上果醬
- 倒杯果汁
但這樣我們會浪費好多時間在發呆等待,大約花了 30 分鐘準備早餐
可是在分秒必爭的早晨,這樣怎麼可以呢!
Async
如果站在程式非同步 Async 的角度來準備:
圖片來源
- 倒杯咖啡
- 加熱煎蛋的鍋子,等鍋子加熱的期間去做下一件事
- 加熱煎培根的鍋子,等鍋子加熱的期間去做下一件事
- 把吐司放進吐司機加熱,讓吐司慢慢加熱的同時,回去煎蛋
- 把蛋煎熟
- 把培根煎熟
- 吐司加熱好了,抹上果醬
- 倒杯果汁
如此一來,我們只花了 15 分鐘準備早餐,就可以多多賴床 15 分鐘了呢!
Sync or Async
一般程式碼是由上往下,一行一行程式碼同步 Sync 去執行的,乍看之下也沒什麼問題,甚至大部分的情境都是用同步的方式去撰寫程式
但若是處理需要等待、不確定需要多久才能完成的任務,ex 存取 I/O、外部資源、HTTP Request…等,就會需要使用非同步來處理了
使用情境
- JavaScript 用 setTimeout 倒數 10 秒後 alert 訊息,但在等待期間可以先做其他事
- JavaScript 發 HTTP Request 等待 Server 回傳資料的期間,可以先做其他事
Callback Function
而非同步中等待完成的任務,會呼叫 Callback Function 來處理接下來要做的事情
Callback Function 是一個被作為參數帶入另一個函式中的「函式」,而這個作為參數帶入的函式將在未來某個時間點被呼叫和執行
而在 AJAX 中也會使用到 CallbackFunction
關於什麼是 AJAX,可以參考我的這篇文章:ASP.NET MVC Framework - AJAX
這裡是一個範例:
1 | $.ajax({ |
這段程式碼其實是這樣運作的:
Server 刪除書本後,Response 回傳給 Client 端,接著才會執行 success 定義的 Callback Function,也就是從 UI 上的 Table 移除該筆資料
而 Callback Function 只會在非同步 Async 任務執行完成後才會觸發,也就是說,只要 Server 一直沒有 Response,那 Callback Function 就永遠不會被執行!
Sync & Async in JavaScript
JavaScript 是一種單執行緒的程式語言,所有的程式碼片段都會在 stack 中被執行
而且一次只會執行一個程式碼片段,也就是一次只做一件事
但這樣會導致在同步 Sync 狀態下,例如 setTimeout、Event Callback、HTTP Request、UI Render 這些需要時間,卻又不確定觸發執行時間的操作,光等他們就飽了?
於是可利用同步 Sync、非同步 Async 概念來處理這些事件,將他們以非同步的方式處理,待執行完同步的程式碼任務後,再來處理這些事件
Sync in JavaScript
但正如剛才提到,JavaScript 是單執行緒的程式語言,則該如何處理非同步事件呢?
- setTimeout、Event、AJAX、UI Render…等等操作,會先被拉出 stack 以避免阻塞
- 前一步驟中被拉出的任務執行完畢後,其 Callback Function 會被丟到工作佇列 Queue 中
- 等到同步 Sync 的程式碼執行完(Stack 清空),才會去撈在佇列 Queue 中的 Function 執行,這個過程稱為 Event Loop
Sync & Async mixed performance
AJAX
再來看看以下這段程式:
1 | $.ajax({ |
他的執行順序是這樣:
- 一開始先執行的 AJAX 先被拉出 stack
- 印出 “想不到吧,是我先被印出”
- 等到 Server Response 後,Callback Function 被丟到工作佇列
- 同步執行的工作都完成後,撈出工作佇列的 Callback Function 執行
- 印出 “Server 已將資料刪除”
所以呀,無論 Server 處理速度多快,就算是瞬間回應,也不會先被印出
與此同理,Event 事件觸發、JavaScript 改變 UI…等,也是如此機制
addEventListener
再來看看這個例子:
1 | document.body.addEventListener('click', function () { |
如果在 3 秒內觸發了 click Event,卻不會馬上印出 “Click!”
而是會等到 3 秒後
印出了 “Hello” 後,才會印出 “Click!”
innerHTML
那麼那麼,再來看看這個例子:
1 | document.body.innerHTML = ""; |
因為渲染 Render 的事件也是被丟到非同步處理,所以會等所有的同步程式碼執行完才會觸發
因此執行的順序是:
等待 3 秒 -> 印出 “Hello” -> document.body.innerHTML 清空畫面
Async VS Multithreading
如此一來,雖然感覺非同步 Async 和多執行緒 Multithreading 感覺很像,都能讓完成所有任務的總時長縮減,但本質上兩者大不相同
Async
- 允許執行緒在等待時間先處理其他作業,透過消除閒置增加效率
- 對於吃 CPU 的作業無法處理(改用多執行緒處理會更有效率)
Multithreading
- 建立多執行緒,將多個任務交給不同執行緒個別處理,利用分工加速完成任務總時長
- 對於等待 I/O 回應的工作幫助不大(改用非同步處理會更有效率)
Reference
- Course 5 FrontEnd Advanced 課程(非公開)