2024-11-04|閱讀時間 ‧ 約 13 分鐘

Day10 JavaScript Async/Await


在JavaScript裡面,Async和Await應該是搜尋熱度最高的關鍵字了,因為他們相對複雜。我們一步步討論這件事的歷史 — 它們為什麼出現,解決了什麼問題。

JavaScript的Synchronous(同步)

首先我們必須了解JavaScript執行的基本原則 — synchronous(同步)。大部分的JavaScript程式都是synchronous的,意味著一行程式碼執行完才會再執行下一行。「同步」這個中文其實有點誤導人,程式碼並不是「同時」執行,而是一行接著一行執行。這會導致什麼問題呢?如果有一行程式碼執行的特別久,後續的就會受影響而延後執行。這個概念被稱為blocking(阻塞)。

我們舉例說明:

用Date.now() + 3000(單位是毫秒)作為結束條件,3秒後,整段程式才會達到結束條件。

我們創建一個function,讓它在3秒內持續運作loop,直到時間到才會結束。執行後我們可以看到Console印出的順序是這樣。

我們會看見'Start',接著卡3秒,然後'Sync task finished',最後才是'End'。

這個例子呈現了因為synchronous的特性,console.log(”End”)這一行被blocked(阻塞)了。


如果JavaScript都只能這樣運作,問題就大了。一個網站常常需要等候外部傳資料進來,如果在這期間什麼其他程式碼都不能執行的話,使用者很快就離開了。舉例來說,我們點開instagram貼文,在等待資料期間,需要執行程式來show loading或執行其他動畫等等,這些都需要程式碼在等待的時候能夠正常運作。

換句話說,我們非常需要asynchronous(非同步)的運作方式,也就是在某些情況下,程式碼可以獨立運作,其他人不用等它結束才能行動。

但同時,我們也需要保留synchronous的運作方式,想像一下instagram至少要等到你的資料到齊了,才能執行「顯示出貼文」這段程式碼。所以我們需要一些方式來達成「那些是需要非同步的,這些不用」的機制。

Asynchronous的解決方案

JavaScript中做到asynchronous的方法不少,而且經歷了幾個階段,asyncawait就是其中比較近期的。我們會依照時間線來介紹這些方法以及他們為何被取代。


Callback Function(回調函數)

又一個新的function名稱,但別緊張。callback function指的就是「在某件事發生之後,再執行的function」,我們以前就看過。

’click’之類的事件發生後,再執行的function就是callback function。


監聽一個事件還好,但如果我們的步驟有4個呢?假設我們需要按照順序讀取4份文件。因為要按照順序,所以我們在每個讀取檔案的function裡加上callback去讀取下一份,看起來會像這樣:

你可以想像如果我們有10個步驟要做情況會有多慘烈。一層包著一層的情況非常難理解,而且程式碼本身會變成一個三角形,非常難閱讀。這種情況被稱作callback hell(地獄)。所以callback不是很好的辦法,慢慢有更好的方法出現,那就是Promise


Promise(承諾)

Promise是JavaScript裡面很特殊的物件,它需要用關鍵字new來創造,基礎語法大概是這樣。

Promise創建式子裡會有一個function,裡面第一個參數是成功時會做的事,第二的是失敗時會做的事。至於什麼情況會成功跟失敗呢,通常這些都是跟伺服器要求資料的時候會用到的工具,伺服器會回傳成功會失敗的資訊給你。現在的重點是知道它的語法如何勝過callback。


接下來繼續改寫這個例子:

換行只是方便閱讀。

一個Promise後面可以接then()catch(),這些是內建的method。resolve()reject()的參數會被傳過去。以上述的例子來說,當Promise成功,success就會等於”Promise結果成功”。如果失敗fail就會等於”Promise失敗原因”(成功的話catch()就不會執行了)。


這裡一次會看見很多東西,包含之前提過的簡寫arrow function還有chain的概念。在Promise後面接的那些(包含then()catch()),會獨自運行(asynchronous),不會block下面的程式碼。


更重要的是,這個then(),可以寫很多串接下去。我們再討論回剛才讀取書籍,把readFile()改成Promise的版本。

在之前我們看見array一直接下去,重點是array必須回傳array才能一直接下去。這裡我們想讓readFilePromise()成為一個chain,就要讓他回傳readFilePromise(),而每個Promise又都有then()可以用,所以可以一直接下去。


從這個改寫範例我們看到,這樣不管寫幾項,程式碼都還是相對好閱讀。Promise雖然已經優化了asynchronous的撰寫,但你也看得出來,它其實也沒那麼好寫,有一堆return和其他關鍵字(不過Promise還是滿常見的)。asyncawait更加簡化了這個流程。


asyncawait

講了這麼久終於到了今天的主題,asyncawait。它們兩個關鍵字的出現讓撰寫非同步程式更像寫同步程式。他們還是需要一個Promise,可以理解成「處理Promise一種更好寫的語法」。

我們只需要在需要用到asynchronous的function前面加上關鍵字async(非同步)就可以了。async function整個都會獨立運作,不會block其他程式的運行。而裡面的每一行就像synchronous一樣一行一行的運作。第一章內容讀取之後,接著第二章以此類推。


值得注意的是,在回傳Promise的函數前加上await(等待)才可以確保每一步都等候結果再進行,正如同它的字面意思,我們在命令JS等這個Promise完成,這可能更符合邏輯需求。如果不需要按照順序,則可以略過await,讓其他操作繼續運行。

如果那個function跟Promise沒有關係,它本來就會依照順序執行,不用特別加await


總而言之,asyncawait提供了一種更自然的方式來處理asynchronous,更像原本synchronous的寫法。雖然還是有一些學習成本跟額外的規則,像是我們還是需要Promise以及特定位置await的使用等等,但回頭看看callback function,你能了解它的益處。

小結

asyncawait現在已經是相對主流處理asynchronous的方式了,雖然其中還是有些比較不直觀的部分,但比起callback hell和冗長的Promise chain,asyncawait已經提供了一種相對易於閱讀、撰寫的解決方案。今天的內容比較深入,但不用擔心。掌握這些語法的優缺點,理解asyncawait的出現如何簡化了asynchronous,已經是很大的一步了。即使一開始有些不直覺,隨著練習和應用,你會逐漸掌握它們的用法,慢慢進步。


最後,我們會用先前所學的來製作一個簡單的To-Do List App,對於初學JavaScript的人來說,To-Do List真的是一個經典練習。裡面會包含許多關鍵的JS應用,即使是大型的網站產品,也常常需要這些語法。透過這個實作練習,你將會更直觀地體驗JavaScript的實際應用。


Resource

今日Codepen

連結

Credits

關於我

我是Erkin, 一個網站開發者。
有任何疑問或是想勘誤的話歡迎聯繫。

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.