异步控制与事件
异步封装本质是小型状态机:决定工作何时启动、谁拥有结果、哪些调用共享工作、何时释放资源。Promise 只组合完成结果,取消信号只提出停止请求;启动限额与界面提交资格都要另建规则。
核心模型
异步封装本质是小型状态机:决定工作何时启动、谁拥有结果、哪些调用共享工作、何时释放资源。Promise 只组合完成结果,取消信号只提出停止请求;启动限额与界面提交资格都要另建规则。
原理与机制
先写时间契约,再写计时器
尾沿防抖只在安静满 wait 后执行最后一次,节流则在持续调用中控制频率;首沿加尾沿需要说明单次调用是否重复、冷却从何时开始。maxWait 的最大期限不能随着每次输入无限后移。普通包装函数保留动态 this,回调使用最后参数,cancel 清理计时器及待处理状态。为防重入,执行用户回调前先登记本轮状态、清掉已取出的 pending,否则回调重新调用包装器时,外层可能覆盖下一轮工作。
启动控制与完成组合是两件事
并发池接收任务工厂,worker 在 await 前领取唯一索引,任务结束才补位,结果写回原索引以保留输入顺序。同步 throw 与异步 reject 都须捕获。Promise.all 则遍历输入、逐项安装处理器,以剩余计数和迭代结束哨兵处理空输入与乱序完成;它不限制已经启动的请求。有限迭代器中途抛错时,已产出的拒绝 Promise 仍要被接管,不能先整体展开后才注册。race 超时也不会停掉输家,补位可能使真实活动数超过 limit。
重试要绑定逻辑操作
有限重试先分类错误:参数、鉴权和业务拒绝通常不重试,暂时网络失败才考虑退避、抖动与总体截止时间,限流还应尊重服务端提示。可写操作必须幂等或使用稳定幂等键,重试沿用同一操作身份,改变载荷不能偷偷复用旧键。AbortSignal 要传给任务和退避等待,取消等待需清计时器与监听器;底层不合作时只能阻止晚到结果提交,不能宣称计算已停止。
共享执行和最新提交各有所有权
single-flight 的 Map 只保存进行中的 Promise,同 key 消费者共享它,成功失败都删除,并按 Promise 身份清理,防止旧任务删掉后来者。失败永不清除会让后续请求永久失败;一个消费者退出也不应直接取消所有消费者。latest-only 则给每轮 generation,新任务先取得身份再取消旧任务,并在启动及所有成功、失败、finally 回写前核对身份。A→B→A 三次文本相同不代表同一意图,序号防住不响应 abort 的旧转换。
事件派发也是可重入调度
Emitter 要声明派发时增删订阅的语义。若按快照执行,本轮新增等下一轮,本轮已入快照的监听器即使被删除仍执行;这不是原生 DOM 模型的复刻。once 在调用用户回调前设 fired 并退订,才能挡住嵌套 emit 与外层旧快照的再次调用。同步异常是否终止本轮、异步拒绝是否聚合也必须明确。组件和任务作用域负责退订、取消与释放流锁,避免事件总线成为无人负责的长期引用。
最小示例
javascript让 A 慢、B 快,即使 A 忽略 signal,只有 B 可提交。再令取消 A 的监听器同步启动 C,可观察 B 在身份复查处退出,C 保持最新;这解释了“先登记新控制器,再取消旧任务”的必要性。commit 约定为短同步操作,调用方负责捕获当前错误。
核验:Node v24.19.0:A慢B快只提交B、取消监听器同步重入时只启动A/C断言通过;不代表网络取消或框架集成已测。
边界与取舍
边界
- 防抖减少启动频率,不能防旧请求覆盖;all 拒绝、race 超时都不会自动取消底层任务。
- 共享 Promise 不是结果缓存;缓存键若缺租户和权限,去重也会造成数据混用。
- 包装器空间可为 O(1),但不合作的旧任务仍会耗资源,不能把不回写等同停止。
取舍
- 并发越高可能缩短总时间,也可能放大连接、内存和限流压力;以真实 active 工作量选择上限。
- 快照派发语义稳定且易解释,但额外占 O(监听器数) 空间;可变遍历省拷贝却更难推理。
口述示范
我先定义启动、共享、取消与提交四类契约,再画重入和乱序时间线。实现时用身份、不变量和资源清理保证状态正确,最后分别测真实并发数、结果顺序和过期回写。
章节练习
参考来源与核验边界
- MDN:AbortSignal · 核验 2026-10-04
- ECMAScript:PerformPromiseAll · 核验 2026-10-04
- MDN:Map · 核验 2026-10-04
2026-10-04 核验。示例不复刻 Promise 子类及全部规范细节;TranslateFlow 为假设演练,不是用户项目实现或结果。
内容版本:2026-10-04.2 · 题目和来源 ID 保持原样,本站不将面经标签解释为企业官方出题或高频保证。