JavaScript 异步编程与请求取消全指南
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
取消不再需要的请求。 - 超时设置避免长时间等待。
- 使用
- 后端:
- 监听客户端断开事件,终止耗时操作。
- 数据库操作设置查询超时。
- 通用:
- 关键业务设计幂等性逻辑(如支付去重)。
总结:异步编程需兼顾效率与资源管理,合理使用取消机制可显著提升应用性能和用户体验。