取消、竞态与流式响应:练习答案
AbortController 取消请求
js
let controller
async function search(query) {
controller?.abort('superseded')
controller = new AbortController()
try {
const response = await fetch(`/search?q=${encodeURIComponent(query)}`, { signal: controller.signal })
return await response.json()
} catch (error) {
if (controller.signal.aborted) return { kind: 'cancelled', reason: controller.signal.reason }
throw error
}
}组件卸载时再次调用 abort;取消是控制流分支,不应显示为服务器错误。生产代码还需把 controller 保存在当前请求身份中,避免旧请求的 catch 误读新 controller。
请求序号与过期响应
每次输入递增 requestId,响应提交前比较本地 id;即使旧请求没有真正取消,也不能覆盖新结果。测试先发慢请求 A,再发快请求 B,最终 UI 必须显示 B,并记录 A 为 ignored。
ReadableStream cancel 与 finally
读取循环在 finally 中释放 reader;用户停止时调用 reader.cancel(),服务端或生产者收到 cancel 后停止继续入队。若 chunk 在 UTF-8 字符中间被切断,复用同一个 TextDecoder({ stream: true }),不能对每个 chunk 单独解码。
章节入口核对
以下入口对应本章概览或综合练习,答案核对沿用本章各知识点的实现与日志。