取消、竞态与流式响应
请求取消有两个责任:AbortController 通知底层释放工作,业务层忽略已经不再属于当前输入的结果。两者都要做,不能只依赖 UI 禁用按钮。
js
let controller
async function search(q) {
controller?.abort()
controller = new AbortController()
const response = await fetch(`/api/search?q=${encodeURIComponent(q)}`, { signal: controller.signal })
return response.json()
}流式响应要逐块读取并处理不完整边界;关闭 reader、取消 signal 和组件卸载必须成对。只测试最终字符串不能证明中途取消已经停止读取。
练习
用两个可控 Promise 重放 old/new 搜索乱序,并记录取消、忽略旧结果和最终状态三条证据。
核心机制与实现
网络请求有请求生命周期、缓存和取消;本地存储有同步 API、配额、版本迁移和跨标签页一致性。
围绕“取消、竞态与流式响应”先写出输入 → 中间状态 → 可观察结果,再实现最小闭环。不要把事件监听、异步请求、DOM 读取或 store 写入混在一个函数里;每个副作用都要有创建、完成、取消和清理路径。
js
const controller = new AbortController()
const request = fetch('/api/items', { signal: controller.signal })
setTimeout(() => controller.abort(), 500)
await request逐步运行:先只验证成功路径,再改变一个输入;记录时间顺序、对象身份、错误类型和最终可见状态。若环境不支持某个浏览器 API,要把静态协议验证和真实浏览器验证分开记录。
失败边界与变体
- 输入为空、重复、过期或非法时,不应静默覆盖有效状态。
- 组件卸载、路由离开、标签页关闭或请求取消时,资源必须停止写入。
- 跨文档、Shadow DOM、Worker、SSR 或多标签页会改变可见边界,不能沿用单页面直觉。
- 需要持久化时记录 schema 版本和迁移失败策略;需要全局状态时先证明局部状态不足。
可运行实验与练习
分别测试成功、HTTP 错误、网络错误、超时取消、重复提交和旧数据迁移;记录请求状态与存储版本。
练习要求:实现一个最小版本,补一个失败测试,再给出一个替代方案及其适用条件。答案应包括实际输入序列、观察结果、修复原因和未覆盖的浏览器差异。