JavaScript 異步編程與請求取消全指南
涵蓋:同步/異步、Promise、async/await、AbortController、前后端協作
一、同步與異步
1. 同步(Synchronous)
- 定義:代碼按順序執行,前一步完成才能執行下一步。
- 特點:阻塞主線程,適合簡單邏輯。
- 示例:
console.log("步驟1"); console.log("步驟2"); // 必須等待步驟1完成
2. 異步(Asynchronous)
- 定義:代碼非阻塞執行,任務完成后通過回調、Promise 或事件通知結果。
- 特點:高效但復雜度高,適合網絡請求、I/O 操作。
- 示例:
setTimeout(() => console.log("異步任務"), 1000); console.log("繼續執行"); // 立即執行
二、Promise 核心知識
1. 基本概念
- 狀態:
Pending
(進行中)、Fulfilled
(成功)、Rejected
(失敗)。 - 創建與使用:
const promise = new Promise((resolve, reject) => {setTimeout(() => resolve("成功"), 1000); }); promise.then(result => console.log(result));
2. 鏈式調用與靜態方法
- 鏈式調用:
fetch(url).then(response => response.json()).catch(error => console.error(error));
- 靜態方法:
Promise.all()
:所有成功返回結果數組。Promise.race()
:返回第一個完成的結果。
三、async/await 進階
1. 基本用法
async
函數:隱式返回 Promise。await
:等待 Promise 完成并提取結果。async function fetchData() {const data = await fetch(url);return data.json(); }
2. 錯誤處理
try...catch
:捕獲異步錯誤。async function safeFetch() {try {const data = await fetch(url);} catch (error) {console.error("請求失敗:", error);} }
3. 并發控制
- 串行:
await task1(); await task2(); // 等待 task1 完成
- 并行:
const [res1, res2] = await Promise.all([task1(), task2()]);
四、請求取消:AbortController
1. 核心功能
- 中斷異步操作(如
fetch
請求)。 - 核心對象:
AbortController
:生成中斷信號。AbortSignal
:傳遞給異步任務。
2. 使用示例
const controller = new AbortController();// 發起請求并傳遞信號
fetch(url, { signal: controller.signal }).catch(error => {if (error.name === "AbortError") {console.log("請求已取消");}});// 用戶點擊取消按鈕
document.getElementById("cancel-btn").onclick = () => controller.abort();
3. 超時自動取消
async function fetchWithTimeout(url, timeoutMs) {const controller = new AbortController();const timeoutId = setTimeout(() => controller.abort(), timeoutMs);try {const response = await fetch(url, { signal: controller.signal });clearTimeout(timeoutId); // 請求成功時清除定時器return response.json();} catch (error) {if (error.name === "AbortError") {throw new Error(`超時(${timeoutMs}ms)`);}}
}
五、后端處理請求取消(Java/Spring Boot)
1. 默認行為
- 客戶端斷開后,服務器繼續執行任務。
- 示例:
@GetMapping("/data") public String longTask() throws InterruptedException {Thread.sleep(5000); // 即使客戶端斷開仍會執行return "完成"; }
2. 主動終止任務
- 監聽連接中斷(使用
DeferredResult
):@GetMapping("/async") public DeferredResult<String> asyncTask() {DeferredResult<String> deferredResult = new DeferredResult<>();Future<?> future = executor.submit(() -> {try {Thread.sleep(5000);deferredResult.setResult("完成");} catch (InterruptedException e) {deferredResult.setErrorResult("已取消");}});// 客戶端斷開時取消任務deferredResult.onCompletion(() -> future.cancel(true));return deferredResult; }
3. 數據庫查詢超時
@Query(value = "SELECT * FROM large_table", timeout = 5) // 5秒超時
List<Record> findLargeData();
六、關鍵問題解答
1. 客戶端取消請求后,后端是否繼續執行?
- 默認繼續執行,需后端主動監聽連接中斷事件并終止任務。
2. 為什么需要 async
函數?
- 語法要求:
await
必須在async
函數內使用。 - 隱式返回 Promise:方便異步操作的值傳遞。
3. 如何避免回調地獄?
- 使用 Promise 鏈式調用或 async/await。
七、最佳實踐
- 前端:
- 使用
AbortController
取消不再需要的請求。 - 超時設置避免長時間等待。
- 使用
- 后端:
- 監聽客戶端斷開事件,終止耗時操作。
- 數據庫操作設置查詢超時。
- 通用:
- 關鍵業務設計冪等性邏輯(如支付去重)。
總結:異步編程需兼顧效率與資源管理,合理使用取消機制可顯著提升應用性能和用戶體驗。