Compose适用于纯函数同步组合,零开销、无死锁、调试清晰;pipeline用于耗时异步阶段,需显式goroutine并提供并发与背压。

func 作为一等值,组合本身不难;真正卡住人的,是组合后的行为一致性、错误传播和执行时机——尤其是你把 Compose 和 pipeline 混用时。
用 Compose 组合纯函数,别碰 channel
如果你只是对内存中的一小段数据做 strings.TrimSpace → strings.ToLower → strings.TrimPrefix,就该用函数组合,而不是启动 goroutine + channel。原因很实在:Compose 零调度开销、无死锁风险、调试路径清晰。
-
Compose接收两个func(A) B和func(B) C,返回func(A) C;所有逻辑在当前 goroutine 同步执行 - 泛型版更安全:用
func[T, U, V any](f func(T) U, g func(U) V) func(T) V,避免 interface{} 类型断言 - 别往
Compose里塞带副作用的函数(比如写文件、发 HTTP 请求)——它不是 pipeline,不提供并发或背压
用 pipeline 组合耗时阶段,必须显式 goroutine
当你需要读文件 → 解析 JSON → 写数据库,并且每个阶段耗时不均(比如解析偶尔卡 200ms),才值得上 pipeline。这时候组合的不是函数签名,而是 func( 这类 channel 工厂函数。
- 每个 stage 必须自己启动 goroutine:
go func() { ... }(),否则上游写入会阻塞在无缓冲 channel 上 - 输入 channel 不能
close(in)—— 消费者只负责读完退出,关闭权永远在生产者手里 - 输出 channel 的
close(out)必须放在 goroutine 内部、for range in循环结束后,且只能 close 一次
错误怎么传?别用 panic,也别只靠 return error
纯函数组合可用 ComposeE(返回 (V, error)),但 pipeline 里 error 是另一套逻辑:它要跨 goroutine、可取消、能中断整条链。
- 不要在每个 stage 里
if err != nil { return }然后静默丢弃——下游还在range out,永远等不到 close - 正确做法:所有 stage 共享同一个
context.Context,并在每个select中监听ctx.Done();同时配一个单独的errCh chan error - 一旦任一 stage 出错,往
errCh发送错误,主 goroutine 收到后调用cancel(),并等待所有 stage 退出再关闭所有 output channel
什么时候该切回函数组合,而不是硬套 pipeline
很多团队过早引入 pipeline,结果调试时发现 90% 的 stage 都是内存计算,反而增加 channel 管理负担和 goroutine 调度抖动。
立即学习“go语言免费学习笔记(深入)”;
- 数据量小(f(g(h(x))) 或
Compose - 某个 stage 明确要并发(比如 4 个 worker 并行校验)、或需反向背压(下游慢时上游自动减速)→ 才上 channel pipeline
- 最常被忽略的信号:你开始给每个 stage 加
make(chan T, 1)缓冲,却没配context.WithTimeout和errgroup.Group—— 这说明你正在用 pipeline 解决本该由函数组合解决的问题
Compose 或怎么起 goroutine,而是判断哪一段逻辑该同步跑、哪一段该拆进独立协程——这个边界,得看数据来源、延迟分布和错误恢复策略,不是看代码“看起来像不像管道”。


















