Promises 可以幫助理解非同步操作的基本處理機制,這對於理解後續的 async/await 很重要,所以先從 promise 開始說起非同步。
| 特性 | 同步 | 非同步 |
|---|---|---|
| 執行方式 | 依次執行 | 允許其他任務在等待操作時執行 |
| 程式碼順序 | 由上而下依序編譯 | 可以在任務完成後透過回呼、Promise 或 async/await 繼續執行 |
| 性能影響 | 可能導致長時間的阻塞,導致使用者過長時間等待畫面出現 | 提高反應性,避免長時間的阻塞 |
| 複雜性 | 通常較簡單,但是因為太簡單,不適合不可預測的任務 | 適合網路請求、計時器,不受時間阻塞的操作任務 |
| 常用方法/函數 | Array.forEach()、Math.max() |
setTimeout()、fetch()、Promises、async/await |
| 範例 | ![]() |
![]() |
| 結果 | 1->2->3 | 1->3->2 |
Promise 是 JavaScript 的內建對象,用來表示一個非同步操作的最終完成或失敗的結果值,主要有三種狀態:
Pending(待定):初始狀態,既不是成功也不是失敗。
Fulfilled(已兌現):操作成功完成。
Rejected(已拒絕):操作失敗。
創建 Promise
使用 Promise 建構函數可以建立一個新的 Promise 物件。
建構函式接受一個執行器函式作為參數,函式有兩個參數:resolve 和 reject;當非同步操作成功時,呼叫 resolve;當非同步操作失敗時,呼叫 reject。
const promiseSetTimeout = (status) => {
return new Promise((resolve, reject) => {
// 建構一個新的 Promise 函式
setTimeout(() => {
if (status) {
resolve("promiseSetTimeout 成功");
} else {
reject("promiseSetTimeout 失敗");
}
}, 0);
});
};
promiseSetTimeout(true).then(function (res) {
console.log(res); // promiseSetTimeout 成功
});
🔔 程式碼補充說明:
呼叫promiseSetTimeout後把狀態參數帶入,然後運行promise過程,接著進入pending狀態,等到pending狀態結束才會往成功或失敗前進。
使用 Promise
可以使用 then 和 catch 方法來處理 Promise 的結果或錯誤。
then:用於處理 Promise 的成功結果。catch:用來處理 Promise 的錯誤。
myPromise
.then((result) => {
console.log(result); // 成功
})
.catch((error) => {
console.log(error); // 失敗
});
Promise chain
Promise chain 是一種使用 .then() 和 .catch() 方法依序處理非同步操作的技術。
API 取得資料後,可能取得第一個後才能取第二個接著第三個,這時候就可以使用 promise 的方法,這有助於避免回調地獄,即嵌套回調變得難以閱讀和管理的情況。
圖片來源:Node 7.6 + Koa 2: asynchronous flow control made right
以下是用 Promise chain 處理多個連續的非同步操作,並且每個 .then 中的處理函數都可以處理前一個 .then 的結果。
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("Data fetched"), 1000);
});
};
const processData = (data) => {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(`${data}and processed`), 1000);
});
};
fetchData()
.then((result) => {
console.log(result); // Data fetched
return processData(result);
})
.then((processedResult) => {
console.log(processedResult); // Data fetched and processed
})
.catch((error) => {
console.error("Error::", error);
});
error handle
在 Promise 鏈中,錯誤處理可以透過 .catch() 捕捉鏈中任何 .then 方法拋出的錯誤,並且可以在鏈的最後進行統一處理。
const promiseSetTimeout = (status) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (status) {
resolve("promiseSetTimeout 成功");
} else {
reject("promiseSetTimeout 失敗");
}
}, 500);
});
};
promiseSetTimeout(true)
.then((res) => {
console.log(1, res);
return promiseSetTimeout(false);
})
.then((res) => {
console.log(2, res);
return promiseSetTimeout(true);
})
.then((res) => {
console.log(3, res);
})
.catch((err) => {
console.log(err);
return promiseSetTimeout(true);
})
.then((res) => {
console.log(`我回來啦~`, res);
});
依照以上程式碼回傳結果順序:1 'promiseSetTimeout 成功’ → promiseSetTimeout 失敗 → 我回來啦~ promiseSetTimeout 成功
因為,Promise 遇到錯誤的時候,就算中間有再多的串接,還是會找最近的 .catch 回傳,中間就直接跳過,如果後面又有串接就會執行。
Promise.all() -> 實戰中使用率比較高,但也相對很雷!面試有被問過!Promise.all() 會同時執行,並且在全部完成後統一回傳陣列,這個陣列的內容也是 promise 中 resolve 的內容。
不過,使用這個方法有個很大的致命傷:如果其中有事件 reject,那麼此 promise 也均視為失敗進入 .catch。
const promise1 = Promise.resolve(8);
const promise2 = new Promise((resolve, reject) =>
setTimeout(resolve, 100, "kuku")
);
const promise3 = 666;
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values); // [8,"kuku",666]
})
.catch((error) => {
console.error("Error:", error);
});
Promise.race() -> 很少用到
使用 Promise.race 傳入多個 promise 事件,這個方法僅會回傳第一個完成的事件。
const promise1 = new Promise((resolve, reject) =>
setTimeout(resolve, 500, "我還要化妝需要 500 ms!")
);
const promise2 = new Promise((resolve, reject) =>
setTimeout(resolve, 100, "我很快,叫我 100ms 先生!")
);
Promise.race([promise1, promise2])
.then((value) => {
console.log(value); // "我很快,叫我 100ms 先生!"
})
.catch((error) => {
console.error("Error:", error);
});