← 返回目录

第 3 卷

第 3 / 4 卷日期:

  }))
}

这里我们看到 Promise.all(...),这代表并发执行所有 disposer。如果我们深入 clear() 方法就会发现,并发执行前还有一个逆序的逻辑。

// utils.ts 里 DisposableList.clear() 的完整实现
clear() {
  const values = [...this.map.values()]   // 取出所有,按注册顺序
  this.map.clear()        // 清空
  return values.reverse()        // 反转成「后注册的先返
}

第三环:单个 disposer 逆序执行自己的清理函数。

// fiber.ts 里 effect() 内部的 dispose()(节选)
for (const disposable of disposables.splice(0).reverse()) { /
//        ↑ splice(0) 清空数组并返回原顺序,reverse()

当我们深入单个 disposer 内部,又看到另一个 reverse(),这里我们要搞清楚,这里的 reverse() 是针对单个 disposer 内的不同步骤。把这两个 reverse() 的逻辑结合起来看,就能得出一个结论:多个 disposer 按注册顺序逆序启动,如果这些 disposer 本身全部都是同步函数,它们会按逆序依次执行,但如果某些 disposer 内部有异步逻辑,那么它们虽然会逆序启动,但并发完成的顺序不能确定。

所以如果你的一组拆除步骤有先后依赖,比如必须先停止轮询,才能关闭连接,正确做法是把它们写进同一个 disposer 里,作为一条清理链依次 await,靠 disposer 内部的“排队”来保证顺序。

fiber.dispose()        ← 触发卸载
        │
        ▼
  _unload()        ← 插件卸载的真正执行者
        │
        ▼
  _disposables.clear()        ← 其内部 values.reverse():逆序
        │
        ▼
  Promise.all 并发启动 wrapper   ← 每个 wrapper 触发它那个 disposer
        │
        ▼
  disposables.splice(0).reverse()  ← 单个 disposer 内部,逆序执

本讲小结

今天这一讲,我们完成了迈向 Cordis 世界的第一步,也是从“传统的手动资源管理方式”到 “动态可组合应用”的重要起点。

我们通过 heartbeat 插件体会到了可撤销副作用的魅力:把 setInterval 和 clearInterval 这对“前进与后退”,写进同一个 ctx.effect,框架便会在插件卸载时替你执行清理函数。沿着这个例子深入源码,我们看到了框架如何在卸载时组织并逆序执行这些清理逻辑。

这背后真正的变化是:你不再手动维护零散的清理代码,而是把“如何建立”与“如何撤销”写在一起,交给框架在卸载时兑现;框架负责调用你提供的清理函数,并按照逆序执行,让环境尽可能回到副作用发生之前的状态。

这,就是“时之可逆”的本质——不是真的逆转时间,而是让每一次副作用都携带一条可靠的 “倒带”路径,使环境能够被干净地回收。

这节课我们解决了“时间”维度的问题,单个组件能否安全地来、安全地走。但前面还有一个更现实的问题等着我们:当多个组件同时存在时,它们之间的依赖关系怎么处理?一个服务下线

注:本讲 GitHub 链接

思考题

  1. 你来写一个插件,ctx.effect 里只做“前进”(比如 setInterval),不 return 清理

函数,然后挂载它、再卸载。Cordis 会不会报错,那个定时器最后谁来清理?

  1. 用一个 effect 依次登记三个资源:先打开文件,再建立连接,最后启动轮询。每个资源都

提供自己的清理函数(关闭文件、断开连接、停止轮询)。那么卸载时,这些清理函数按什么顺序执行?为什么?

欢迎你把运行结果分享出来。如果你觉得这节课对你有帮助,也欢迎分享给其他朋友,我们下节课再见!

© 版权归极客邦科技所有,未经许可不得传播售卖。页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

全部留言 (7)

最新精选

图 9-1
图 9-1

邋遢的流浪剑客

2026-08-26 来自上海

思考题使用dsh写代码验证结果如下:

1)ctx.effect 里启动 setInterval 但不返回 disposer 时,Cordis 不会报错;插件卸载后定时器仍然继续 tick。原因是 Cordis 只管理 effect 显式返回或 yield 出来的清理函数,不会自动追踪 JS runtime 资源。

2)一个 effect 依次登记“关闭文件、断开连接、停止轮询”三个清理函数时,卸载顺序是反向的:先停止轮询,再断开连接,最后关闭文件。Cordis 内部用 disposer 数组收集,卸载时 reverse() 后执行,也就是 LIFO 后进先出

作者回复: 赞👍

图 9-2
图 9-2
全站分卷索引 · 4 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷