本文想淺談 JavaScript 非同步寫法的演進。現在我們常看到的 `async` / `await` 是什麼時候出現的呢?為什麼需要這種語法呢?早期怎麼寫非同步的函式呢?接下來我們一起認識早期到現在的 JavaScript 非同步語法。 ## callback function 回呼函式 一開始 JavaScript 是沒有 `Promise` 物件的,所以如果要執行非同步操作(例如: `setTimeout`、`XMLHttpRequest`),都是藉由「傳入函式作為參數」來在特定時機執行。我解釋成: > 請幫我在特定時機,執行我給你的函式! ```javascript function getData(callback) { setTimeout(() => { callback('資料來了(callback)'); }, 1000); } getData(function(data) { console.log(data); }); ``` 這裡的 callback 不是保留字,可以用成自訂的名稱。多個 callback 組合起來容易導致 callback hell,會變得難以維護也不好閱讀。 ## Promise 物件 為了解決 callback 會有的問題,ES6 / 2015 年提出了 `Promise` 物件,並搭配 `.then()` 與 `.catch()` 的語法,來管理 Promise 的後續執行。我們可以把 `Promise` 理解成: > 一個承諾,一旦有結果不論好壞都會告知並結束。 ```javascript function getData() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('資料'); }, 1000); }); } getData() .then((data) => console.log(data)) .catch((err) => console.error(err)); ``` `Promise` 物件有 `pending`、`fulfilled`、`rejected` 三種狀態。創立 `Promise` 物件時是 `pending` 狀態,可以設定「事件解決」與「事件失敗」的回呼函式,「解決」後就會變成 `fulfilled` 狀態,「失敗」的話就會變成 `rejected` 狀態。 一旦「解決」後程式就會進入 `.then()`,而「失敗」則會進入 `.catch()`。 ```javascript Promise((「[解決], [拒絕]) => { if (true) { [解決](); // Promise 完成,執行 then } else { [拒絕](); // Promise 完成,執行 catch } }) ``` ## async/await 然而,`Promise` 的寫法仍然不夠直覺,特別是當需要多層 `.then()` 時,程式碼會變得冗長且不易閱讀。為了讓非同步程式碼更像同步的寫法,ES2017 加入了 `async` / `await` 語法。我們可以把這兩個理解成 > await:等要到結果才傳給你 > async:把 function 變成一個承諾 ```javascript async function showData() { try { const data = await getData(); console.log(data); } catch (err) { console.error(err); } } ``` `await` 是一個一元運算子,用來等待一個 `Promise` 的結果。當 `await` 後面是一個 `Promise`,它會「暫停」函式的執行直到該取得回傳值。 而如果函式內用到 `await` 時,整個函式就需要標示 `async`,這個函式便會變成一個 `Promise` 物件。其他引用這個函式的時候也要用 `await` 等它完成。 除錯時可以用 `try` / `catch` 來執行,只要 `try` 內的程式有出錯,就會執行 `catch` 內的函式。 ### 參考資料: [卡斯柏's Blog - Async function / Await 深度介紹](https://www.casper.tw/development/2020/10/16/async-await/) [MDN - Promise](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise) [MDN - await](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/await) [MDN - async function](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/async_function) [MDN - try...catch 語法](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/try...catch)