尧图精选

深入 Effect Atom 空闲 TTL 清理机制:@effect/atom-solid RegistryProvider 的 defaultIdleTTL 可选化解析

🕒 发布时间:2026/9/14 10:59:13 📁 来源:尧图网络
深入 Effect Atom 空闲 TTL 清理机制effect/atom-solid RegistryProvider 的 defaultIdleTTL 可选化解析【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effectEffect 的 Atom 模块为 TypeScript 应用提供了细粒度的响应式状态管理而effect/atom-solid将其与 SolidJS 的响应式运行时对接。本仓库近期的一条 changeset.changeset/pre/atom-solid-idle-ttl.md宣布了一个值得关注的 patch 变更RegistryProvider现在允许defaultIdleTTL保持未定义undefined与 React 绑定保持一致从而在没有显式配置 TTL 时让未使用的 Atom 被立即清理。本文将围绕这条变更结合仓库源码AtomRegistry、Atom、Solid/React 绑定完整剖析 idle TTL 的配置语义、底层清理生命周期与实战用法帮助读者准确掌握 Effect Atom 的内存回收时机。一次 patch 变更defaultIdleTTL 从强制走向可选该 changeset 的完整原文只有一句话但信息密度很高--- effect/atom-solid: patch --- Allow RegistryProvider to leave defaultIdleTTL undefined, matching the React binding and enabling immediate cleanup of unused atoms unless a TTL is explicitly configured.拆解来看它包含三层语义类型层面RegistryProvider的defaultIdleTTL选项此前在 Solid 绑定中被视为必填或默认有值现在允许显式传undefined行为层面当defaultIdleTTL未定义时注册表不会为未使用的 Atom 安排延迟回收而是立即清理immediate cleanup对齐层面Solid 绑定与 React 绑定在 API 形态上保持一致——两者都允许该选项留空具体默认行为则由各绑定自身的上下文决定。要理解这条变更为什么重要需要先弄清defaultIdleTTL在 Effect Atom 体系中的位置。背景Atom 与 AtomRegistryEffect 的响应式状态模型由两部分组成Atom一个响应式状态单元包含read读取函数、可选的write写入函数、keepAlive是否常驻和idleTTL空闲存活时间等字段定义见 packages/effect/src/unstable/reactivity/Atom.tsAtomRegistry负责存储 Atom 的节点Node、缓存当前值、追踪依赖关系、执行写入与刷新、管理订阅并在节点不再被使用时将其回收。每个 registry 相互独立同一个 Atom 在不同 registry 中可以持有不同值定义见 packages/effect/src/unstable/reactivity/AtomRegistry.ts。在 UI 绑定React/Solid中RegistryProvider为组件子树创建一个AtomRegistry组件内的useAtomValue、useAtom等 Hook 通过 context 共享同一个注册表。注册表的生命周期直接决定 Atom 何时被求值、何时被缓存、何时被回收。defaultIdleTTL 在 AtomRegistry 中的角色AtomRegistry.make的选项签名如下AtomRegistry.tsexport const make ( options?: { readonly initialValues?: Iterablereadonly [Atom.Atomany, any] | undefined readonly scheduleTask?: ((f: () void) () void) | undefined readonly timeoutResolution?: number | undefined readonly defaultIdleTTL?: number | undefined } | undefined ): AtomRegistry其中defaultIdleTTL是注册表级别的空闲存活时间兜底值它决定了一个 Atom 在失去所有订阅者之后还能在缓存中存活多久。同样的选项也出现在AtomRegistry.layerOptions用于 Effect Layer 场景中。在RegistryImpl构造函数里defaultIdleTTL还会影响另一个关键参数timeoutResolutionAtomRegistry.tsthis.defaultIdleTTL defaultIdleTTL if (timeoutResolution undefined defaultIdleTTL ! undefined) { this.timeoutResolution Math.round(defaultIdleTTL / 2) } else { this.timeoutResolution timeoutResolution ?? 1000 }也就是说当你显式传入defaultIdleTTL而未指定timeoutResolution时注册表会自动把超时桶的分辨率设为 TTL 的一半例如 TTL 为 400ms则timeoutResolution为 200ms若两者都未提供timeoutResolution回退到 1000ms。这个推导关系是理解后续清理粒度的关键。空闲清理的完整生命周期immediate cleanup 的底层原理changeset 所说的立即清理对应的是AtomRegistry中一条明确的判断分支。整个生命周期可以概括为节点创建当 Atom 首次被订阅/读取时ensureNode创建节点若 Atom 不是keepAlivecreateNode会立即调度一次无订阅者即移除的任务AtomRegistry.ts订阅结束当最后一个订阅者取消订阅时subscribe返回的清理函数会检查node.canBeRemoved并调度scheduleNodeRemovalAtomRegistry.tsTTL 判定removeNode通过atomHasTtl决定走哪条路AtomRegistry.tsatomHasTtl(atom: Atom.Atomany): boolean { return !atom.keepAlive atom.idleTTL ! 0 (atom.idleTTL ! undefined || this.defaultIdleTTL ! undefined) }若没有 TTL原子自身idleTTL为 undefined 且注册表defaultIdleTTL为 undefinedremoveNode直接删除节点、解除依赖、触发onNodeRemoved不做任何延迟AtomRegistry.ts若有 TTL进入setNodeTimeout把节点挂入时间桶等 TTL 到期后由sweepBucket统一清扫。桶式超时setNodeTimeout以timeoutResolution为粒度把到期时间对齐到桶边界Math.ceil(idleTTL / timeoutResolution) * timeoutResolution并为每个桶创建一个setTimeoutAtomRegistry.ts。这样做的好处是多个原子可以在同一时刻集中回收减少定时器数量扫桶sweepBucket到期后遍历桶内节点对仍可移除的节点执行删除AtomRegistry.ts。由此可以看到changeset 描述的启用未使用原子的立即清理并非语义含糊的营销话术当defaultIdleTTL为 undefined 时atomHasTtl返回 false整个超时桶机制被跳过Atom 在失去订阅的下一轮调度即被回收。原子级 idleTTLsetIdleTTL 与 keepAlive 的优先级除了注册表级的defaultIdleTTL单个 Atom 还可以通过setIdleTTL设置自己的 TTLAtom.tsexport const setIdleTTL: { (duration: Duration.Input): A extends Atomany(self: A) A A extends Atomany(self: A, duration: Duration.Input): A } dual(2, (self, durationInput) { const duration Duration.fromInputUnsafe(durationInput) const isFinite Duration.isFinite(duration) return Object.assign(Object.create(Object.getPrototypeOf(self)), { ...self, keepAlive: !isFinite, idleTTL: isFinite ? Duration.toMillis(duration) : undefined }) })两点值得注意有限时长→ 设置idleTTL毫秒无限时长→ 相当于keepAlive: true永不因空闲而回收在注册表内部实际使用的 TTL 是node.atom.idleTTL ?? this.defaultIdleTTLAtomRegistry.ts即原子级配置优先注册表级配置兜底。与之配套的还有Atom.keepAliveAtom.ts用于让原子即使无人订阅也保持缓存。keepAlive、idleTTL、defaultIdleTTL三者共同构成了一套完整的回收策略矩阵原子 idleTTL注册表 defaultIdleTTL原子 keepAlive空闲回收行为undefinedundefinedfalse无订阅后立即回收本次变更开启的路径undefined有值false等待 defaultIdleTTL 到期后回收有值任意false按原子自身 idleTTL 回收任意任意true永不因空闲回收Solid 与 React 绑定的对比从不一致到对齐本次变更的核心动机是matching the React binding。先看 Solid 侧的RegistryProviderpackages/atom/solid/src/RegistryContext.tsexport const RegistryProvider (options: { readonly children?: JSX.Element | undefined readonly initialValues?: Iterablereadonly [Atom.Atomany, any] | undefined readonly scheduleTask?: ((f: () void) () void) | undefined readonly timeoutResolution?: number | undefined readonly defaultIdleTTL?: number | undefined }) { const registry AtomRegistry.make({ scheduleTask: options.scheduleTask, initialValues: options.initialValues, timeoutResolution: options.timeoutResolution, defaultIdleTTL: options.defaultIdleTTL }) onCleanup(() registry.dispose()) return createComponent(RegistryContext.Provider, { value: registry, get children() { return options.children } }) }变更后defaultIdleTTL?: number | undefined被完整透传给AtomRegistry.make。而 Solid 侧的默认context无 Provider 时是直接AtomRegistry.make()即不带任何选项、defaultIdleTTL为 undefined——这意味着默认情况下 Solid 应用会走立即清理路径。React 侧则有所不同packages/atom/react/src/RegistryContext.tsexport const RegistryContext React.createContextAtomRegistry.AtomRegistry(AtomRegistry.make({ scheduleTask, defaultIdleTTL: 400 }))React 的默认 context 显式提供了defaultIdleTTL: 400毫秒即默认保留 400ms 的空闲缓冲但其RegistryProvider同样允许defaultIdleTTL为 undefinedpackages/atom/react/src/RegistryContext.ts。也就是说允许 undefined 在两个绑定中达成一致而未配置时的默认行为仍由各绑定自身的默认 context 决定Solid 立即清理React 默认 400ms。本次变更的核心价值在于让 Solid 用户能够显式选择零缓冲、立即回收策略而不是被强制套用一个默认 TTL。实战在 Solid 应用中配置空闲回收策略安装依赖以仓库中 packages/atom/solid/package.json 与 README 为准npm install effectrc effect/atom-solidrc场景一希望未使用的 Atom 被立即回收本次变更开启的路径不传defaultIdleTTL即可import { RegistryProvider, useAtomValue } from effect/atom-solid import { Atom } from effect/unstable/reactivity const count Atom.make(0) function App() { return ( RegistryProvider {/* 当 CountView 卸载后count 节点在下一轮调度中被回收 */} CountView / /RegistryProvider ) } function CountView() { const value useAtomValue(() count) return span{value}/span }此时RegistryProvider内部创建的是AtomRegistry.make({ defaultIdleTTL: undefined })atomHasTtl恒为 false只要原子自身没有idleTTL组件卸载后原子状态立即释放——非常适合视图即生命周期的组件内局部状态。场景二希望保留一定空闲缓冲避免频繁重建显式传入毫秒值即可RegistryProvider defaultIdleTTL{2000} CountView / /RegistryProvider如上文源码所示此时注册表会自动把timeoutResolution推导为 1000ms2000 / 2Atom 在失去订阅者后最多存活约 2 秒期间重新订阅会直接命中缓存ensureNode会调用removeNodeTimeout取消待回收的定时器AtomRegistry.ts。场景三针对单个 Atom 精细化控制不修改注册表而是用Atom.setIdleTTL直接给原子设置 TTLconst count Atom.setIdleTTL(Atom.make(0), 5 seconds)原子级配置优先级高于注册表级defaultIdleTTL适合全局默认立即回收、个别热点原子延长保留的混合策略。注意事项与最佳实践Provider 选项不是响应式的Solid 的RegistryProvider在创建注册表时一次性消费scheduleTask、initialValues、timeoutResolution、defaultIdleTTL后续改动不会触发注册表重建源码注释 Provider options are consumed when the registry is created见 packages/atom/solid/src/RegistryContext.ts。如需动态调整 TTL应通过原子的idleTTL字段或重建 Provider 实现。自定义调度器需返回安全的取消函数scheduleTask的返回值会在 Solid 清理期间被调用应保证幂等且可安全重复调用。dispose 的自动执行RegistryProvider通过onCleanup(() registry.dispose())在组件销毁时回收整个注册表因此只要 Provider 正常卸载所有挂载在该注册表上的原子节点都会被dispose一并清空AtomRegistry.ts。timeoutResolution 的粒度影响若显式指定defaultIdleTTL实际回收时刻会按timeoutResolution向上取整对齐真实回收时间可能略晚于 TTL若不希望出现取整可同时显式设置较小的timeoutResolution。测试中的验证方式仓库测试packages/atom/solid/test/index.test.tsx展示了如何手工构造AtomRegistry.make()并通过RegistryContext.Provider注入例如useAtomInitialValues测试中验证初始值只应用一次。这些用例可作为理解注册表行为的参考。小结.changeset/pre/atom-solid-idle-ttl.md这条 patch 变更虽然短小却修正了 Solid 绑定与 React 绑定在RegistryProviderAPI 上的一致性并为 Solid 用户打开了未配置 TTL 即立即回收未使用 Atom的内存管理路径。透过AtomRegistry源码可以看到这一行为根植于atomHasTtl的判定与超时桶机制的取舍无 TTL 时零延迟删除有 TTL 时按timeoutResolution对齐批量回收。理解这套机制可以帮助你在实际应用中按需组合注册表级defaultIdleTTL、原子级idleTTL与keepAlive精确控制 Effect Atom 的状态生命周期与内存占用。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →