Effect 的 Latch 是倒数器吗?

如果你熟悉其他语言的 CountDownLatch,看到 Effect 的 Latch 多半会以为它是个倒数器。我在 effect 4.0.5 里跑了一遍,它不是。

不是。v4 的 Latch 是一个可反复开关的闸门:Latch.make(open?) 默认关闭,open 放行所有当前和之后的等待者,close 再次关闭。

下面先列出各个方法的语义,再用一个暂停和恢复的工作循环验证,最后说它适合做什么。Latch 在 4.0.5 里标注为 @stability stable。

Latch 的几个方法分别做什么?

  • open:打开闸门,放行所有当前和之后的等待者
  • close:再次关闭
  • release:只放行当前等待者,不改变开关状态
  • await:阻塞到被放行
  • whenOpen(effect):等闸门放行后再执行 effect
  • isOpen():查询开关状态

怎么验证闸门可以反复开关?

下面的工作循环在闸门关闭时不推进,打开后推进,再关闭又冻结:

import { Effect, Fiber, Latch } from "effect"

const program = Effect.gen(function*() {
  const gate = yield* Latch.make()
  console.log("default open?", gate.isOpen()) // false

  let ticks = 0
  const worker = yield* Effect.forkChild(
    Effect.forever(
      gate.whenOpen(
        Effect.sync(() => ticks++).pipe(Effect.andThen(Effect.sleep("5 millis")))
      )
    )
  )

  yield* Effect.sleep("30 millis")
  console.log("while closed:", ticks) // 0

  yield* gate.open
  yield* Effect.sleep("50 millis")
  console.log("while open, progressed:", ticks > 3) // true

  yield* gate.close
  yield* Effect.sleep("10 millis")
  const frozen = ticks
  yield* Effect.sleep("50 millis")
  console.log("closed again, frozen:", ticks - frozen <= 1) // true

  yield* Fiber.interrupt(worker)
})

Effect.runPromise(program)

release 和 open 有什么区别?

release 只放行已经在等的 fiber,之后新来的仍会阻塞。我试过在 release 之后新启动的 fiber,确实继续被挡在门外,直到 open。想要「一次性放行所有人并保持开放」,用 open;release 更像「脉冲」。

Latch 适合用在哪?

典型用途是:启动阶段等待依赖就绪后再放行请求;运维开关(暂停消费、维护窗口);测试里让多个 fiber 在同一个起跑线上同时开始。

它和限流结合时,职责是「一刀切的总开关」:当下游熔断或进入维护时 close 整个工作循环,恢复后 open,限速则仍然交给 RateLimiter,见 Effect 的 RateLimiter 怎么按 key 限速?。

版本是 effect 4.0.5,暂停与恢复的示例以及 release 之后新 fiber 仍被挡住这一点,我在这个版本上运行确认过。启动等待依赖、测试起跑线这几个用途是我的使用建议,没有单独写示例运行。@stability stable 的标注读自该版本的 JSDoc,后续版本可能变化。