Effect 深度课effect@4.1.4 · LTS
流、调度与工程化

L21 Stream:零到多个值与背压

⏱ 35 min难度 3/5前置:L20
本课目标
  • 区分 Effect 与 Stream
  • 理解 pull-based 背压为什么天然成立
前置课时尚未完成:L20

可以自行跳转学习,但按顺序推进会更省力;学情分析会把这类缺口归入「前置缺口」。

Effect 恰好一个值,Stream 零到无数个

Stream<A,E,R> 是 Effect 的延伸:同样的三通道,但能发出 0 个、1 个、有限多个甚至无限多个 A。它可替代 Observable、Node stream、AsyncIterable,且与 Effect 共用同一套错误、依赖、中断与资源语义。

基础用法
← 代码可左右滑动 →
import { Effect, Stream } from "effect"

const numbers = Stream.range(1, 10)
const doubled = numbers.pipe(Stream.map((n) => n * 2))

await Effect.runPromise(Stream.runCollect(doubled))
// [2, 4, 6, ..., 20]

const infinite = Stream.iterate(1, (n) => n + 1)
const firstFive = infinite.pipe(Stream.take(5), Stream.runCollect)
💡 runCollect 是众多 Sink 之一:它决定「如何消费」这条流。
🔬 背压来自 pull

流不是生产者随便往里推,而是下游按需 pull 一个 chunk。消费者慢,上游就停下来等——背压因此是模型的自然结果,不需要额外的缓冲阈值配置。Chunk 是批量传输单位,减少逐元素调度开销;底层由 Channel 统一描述「读/写/结束」的状态机。

交互演练
关键术语
Chunk
Stream 批量传输的数据块
Sink
流的消费者,如 runCollect / runDrain
常见误区
× 把无限流直接 runCollect,等价于让程序永不结束。
随堂测验(即时反馈,答题计入掌握度)
Q1Stream 相对 Effect 的关键差别?
Q2pull-based 模型如何带来背压?