已发布 上游基线 bf46254 原文 ↗ 在 GitHub 编辑

资源管理型 Stream

学习如何在 Stream 中管理资源:安全地获取与释放、用于清理任务的终结处理(finalization),以及确保在终结之后执行清理动作,从而在流式应用中稳健地处理资源。

由 Stream 获取的资源必须在 Stream 被消费的整个期间保持打开。组合使用 Effect.acquireReleaseStream.fromEffectStream.scoped,即可把资源的生命周期绑定到 Stream 上。当 Stream 只需要一个 finalizer 时,使用 Stream.ensuring

获取与释放

下面的示例获取一个文件,逐行发出其内容,并在 Stream 消费结束时关闭它。

import { Stream, Console, Effect } from "effect"

// Simulating File operations
const open = (filename: string) =>
  Effect.gen(function* () {
    yield* Console.log(`Opening ${filename}`)
    return {
      getLines: Effect.succeed(["Line 1", "Line 2", "Line 3"]),
      close: Console.log(`Closing ${filename}`),
    }
  })

const stream = Stream.scoped(
  Stream.fromEffect(
    Effect.acquireRelease(open("file.txt"), (file) => file.close),
  ),
).pipe(Stream.flatMap((file) => Stream.fromIterableEffect(file.getLines)))

await Effect.runPromise(Stream.runCollect(stream)) // => ["Line 1", "Line 2", "Line 3"]

Effect.acquireRelease 会把 file.close 注册到 Stream.scoped 创建的 Scope 中。因此,在 Stream.fromIterableEffect 发出文件内容期间,文件会一直保持打开。

终结处理

Stream.ensuring 会在 Stream 自身的 finalizer 之后运行一个 finalizer,无论 Stream 是成功、失败还是被中断。

import { Stream, Console, Effect } from "effect"

const application = Stream.fromEffect(Console.log("Application Logic."))

const deleteDir = (dir: string) => Console.log(`Deleting dir: ${dir}`)

const program = application.pipe(
  Stream.ensuring(
    deleteDir("tmp").pipe(
      Effect.andThen(Console.log("Temporary directory was deleted.")),
    ),
  ),
)

await Effect.runPromise(Stream.runCollect(program)) // => [undefined]