Effect 里会过期的资源怎么刷新?

同一个 LayerRef,先读两次版本号都是 1,invalidate 之后是 2,refresh 之后是 3,缓存命中时没有重建,失效和刷新各让版本前进一次。

单个值需要手动或定时刷新(token、远程配置),用 Resource;单个 Layer 构建出的服务需要失效重建,才用 LayerRef。 Resource 更轻,能用就别上 Layer。

两者都是「单个资源加刷新」,区别在抽象层级:LayerRef 缓存的是一个 Layer 构建出的整个 Context,Resource 缓存的是一个 Effect 的结果。资源的获取和释放仍然靠 Scope,不熟的话先看 Effect 怎么保证资源一定被释放?;需要按 key 区分资源的话,看 Effect 的 LayerMap、RcMap、Pool 该怎么选?。先看一段能跑的代码。

LayerRef 和 Resource 的刷新长什么样?

import { Context, Effect, Layer, LayerRef, Resource, Schedule } from "effect"

let n = 0
class Cfg extends Context.Service<Cfg, { readonly version: number }>()("app/Cfg") {
  static readonly layer = Layer.effect(Cfg, Effect.sync(() => Cfg.of({ version: ++n })))
}
const readVersion = Cfg.use((cfg) => Effect.succeed(cfg.version))

const program = Effect.gen(function*() {
  const ref = yield* LayerRef.make(Cfg.layer, { idleTimeToLive: "1 second" })
  const a = yield* readVersion.pipe(Effect.provide(ref.get))
  const b = yield* readVersion.pipe(Effect.provide(ref.get))
  yield* ref.invalidate
  const c = yield* readVersion.pipe(Effect.provide(ref.get))
  yield* ref.refresh
  const d = yield* readVersion.pipe(Effect.provide(ref.get))
  console.log("LayerRef", a, b, c, d)

  let tick = 0
  const manual = yield* Resource.manual(Effect.sync(() => ++tick))
  const x = yield* Resource.get(manual)
  const y = yield* Resource.get(manual)
  yield* Resource.refresh(manual)
  const z = yield* Resource.get(manual)
  console.log("manual", x, y, z)

  const auto = yield* Resource.auto(Effect.sync(() => ++tick), Schedule.spaced("100 millis"))
  const first = yield* Resource.get(auto)
  yield* Effect.sleep("350 millis")
  const later = yield* Resource.get(auto)
  console.log("auto", first, later)
})

Effect.runPromise(Effect.scoped(program))

输出是 LayerRef 1 1 2 3 和 manual 1 1 2:缓存命中不重建,invalidate 与 refresh 各让版本号前进一次。auto 的 later 比 first 大,说明后台在刷新;精确的刷新次数取决于调度,我没有把它当作契约。

Resource 的 manual 和 auto 有什么区别?

Resource 更轻:Resource.manual 由你调用 refresh,Resource.auto 按 Schedule 在后台刷新,Resource.get 永远返回当前缓存值。token、远程配置快照这类「一个值会过期」的需求,用它就够了。

LayerRef 比 Resource 多了什么?

LayerRef 是无 key 版的 LayerMap,底层是 RcRef。除了 invalidate,还有 refresh(失效并立即重新获取),以及 invalidationSchedule 做周期性失效。

要记住一条:失效不会撤销已借出的 context,它们在各自 scope 关闭前仍可用。

两个 API 稳定吗?

Resource 是 stable;LayerRef 是 unstable,小版本里 API 可能变,用的时候建议收在一个模块里。标签含义见 Effect v4 的稳定性标注:@stability 标签取代 unstable 目录。

版本是 effect@4.0.5。LayerRef 1 1 2 3 和 manual 1 1 2 两行输出在这个版本上跑过;auto 只确认了后台会刷新,没有验证具体刷新次数。稳定性标签来自扫描该版本 dist/*.d.ts 里的 @stability。