在前端開發中,非同步處理是一個非常重要的概念
當程式需要等待資料,例如從伺服器抓資料、等待使用者輸入、計時器完成等等的時候,通常不希望整個頁面因此卡住,這個時候就會用到Promise與async/await
由於JavaScript是單執行緒的語言,它一次只能做一件事,所以如果某段程式需要花時間(ex:讀資料/等待伺服器回應),那其他程式就得排隊等它做完,這會造成整個網頁的畫面卡住
為了解決這個問題,JavaScript採用了非同步執行的機制,讓我們可以先發出請求(例如抓API),然後等資料回來再執行下一步,這樣不會阻塞整個流程讓畫面卡住
在早期,非同步操作通常用回呼函式(callback function)來處理
ex:
getData(function (result) {
processData(result, function (processed) {
saveData(processed, function () {
console.log("全部完成!");
});
});
});
這樣的寫法雖然可行,但層層巢狀、難以閱讀與維護,俗稱回呼地獄
為了解決回呼地獄的問題,ES6引入了Promise
Promise的意思是當某個操作最終成功或失敗,並在完成後執行與結果相應的動作
Promise有三種狀態:
1.pending(進行中)
2.fulfilled(成功)
3.rejected(失敗)
範例:
const promise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("資料載入成功!");
} else {
reject("載入失敗!");
}
});
promise
.then((message) => {
console.log(message);
})
.catch((error) => {
console.error(error);
});
這樣寫的好處是,我們不用層層巢狀,只要呼叫.then()
和.catch()
雖然Promise改善了可讀性,但.then()
鏈仍然會讓程式變得冗長
於是ES8引入了async
/await
,讓非同步程式碼看起來幾乎像同步一樣簡潔
範例:
js
function getData() {
return new Promise((resolve) => {
setTimeout(() => resolve("資料載入完成"), 1000);
});
}
async function showData() {
console.log("開始載入...");
const result = await getData(); // 等待Promise完成
console.log(result);
console.log("載入結束");
}
showData();
輸出順序會是:開始載入...>(1 秒後)>資料載入完成>載入結束
在使用async
/await
時,也可以用try...catch
來捕捉錯誤,會比.catch()
更直覺
範例:
async function fetchData() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("發生錯誤:", error);
}
}
整理一下今天的內容:
概念 | 時代 | 特點 |
---|---|---|
Callback | 早期 | 容易陷入巢狀結構 |
Promise | ES6 | 支援鏈式呼叫,提升可讀性 |
async/await | ES8 | 可用同步風格撰寫非同步程式 |