前兩篇我們學會了 JavaScript 的基礎語法,以及如何用 DOM 與事件讓網頁互動。但在實際開發中,網頁常常需要「等待」某些事情完成,例如向伺服器要資料、讀取檔案、等待計時器。這一篇要介紹的就是處理等待的方法:非同步程式設計,包括 Promise、async/await 與 fetch。
一、同步與非同步
JavaScript 一次只能做一件事(單執行緒)。如果每個動作都要等前一個做完,遇到需要等待的工作,整個網頁就會卡住。因此,耗時的工作會被丟到一旁等待,其他程式碼先繼續執行,這就是非同步。
先看一個經典的例子,請猜猜輸出順序:
console.log("1. 開始");
setTimeout(function () {
console.log("2. 兩秒後執行");
}, 2000);
console.log("3. 結束");
實際輸出順序是:1 → 3 → 2。setTimeout 把工作交給瀏覽器計時,JavaScript 不會停下來等它,而是先執行後面的程式碼,時間到了再回頭執行回呼函式(callback)。
二、回呼地獄(Callback Hell)
早期處理非同步只能靠回呼函式。當多個非同步動作需要依序執行時,程式碼會一層包一層,難以閱讀與維護:
setTimeout(function () {
console.log("步驟 1 完成");
setTimeout(function () {
console.log("步驟 2 完成");
setTimeout(function () {
console.log("步驟 3 完成");
}, 1000);
}, 1000);
}, 1000);
為了解決這個問題,ES6 引進了 Promise。
三、Promise
Promise 代表「一個未來才會完成的結果」,就像餐廳給你的取餐號碼牌:現在還沒有餐點,但保證之後會通知你成功或失敗。它有三種狀態:
| 狀態 | 說明 |
|---|---|
| pending | 等待中,尚未有結果 |
| fulfilled | 成功完成 |
| rejected | 失敗 |