go 中应避免模拟链式 future,而用 select 配合多个 channel 实现“前一个异步结果就绪即启动下一个”的自然流程。

Go 里没有链式 Future,强行模拟 then 或 catch 只会增加复杂度、掩盖 goroutine 和 channel 的真实意图。你要的只是“上一个异步结果出来后,立刻启动下一个”,用 select + 多个 就能干净实现。
为什么不要写 func() *Future 封装
社区有些包(比如 gofuture)提供 .Then() 方法,但实际踩坑频繁:
-
panic: send on closed channel:多数因.Then()内部又启 goroutine,且对 channel 关闭时机判断错误 - 类型不安全:靠
interface{}传参,字段访问错误只能到运行时报,编译期拦不住 - 隐式调度:你调一次
.Then(),底层可能再起一个 goroutine,调度不可控,ctx.Done()也传不进去 - channel 被当成“可重复读取的对象”:第二次
读到零值,误判为有效结果
select 是 Go 链式等待的唯一正解
多个异步任务有依赖关系时,不用嵌套回调,也不用封装新类型,直接用 select 等待前一个结果,再发下一个:
- 前一个任务完成,
select立刻收到值,马上启动后续 goroutine - 所有 channel 必须是只读的(
),避免调用方误写入 - 每个 channel 缓冲大小设为 1,防止 goroutine 启动后卡在发送端
- 必须
defer close(ch),否则接收方无法区分“失败退出”和“还在跑”
func fetchUser(id int) func fetchPosts(uid int) <p>// 链式调用:先取用户,再取其帖子 func getUserPosts(id int) </p><h3>错误必须随结果一起发出,不能丢</h3><p>只用 <code>chan int</code> 无法区分“成功返回 0”和“出错没返回”,这是新手最常翻车的地方:</p>
- 别用两个 channel 分开传值和错误(
chan T+chan error),调用方容易漏掉select的错误分支 - 推荐统一用
chan Result[T]:定义type Result[T any] struct { Value T; Err error } - 发送方无论成功失败都发一次,接收方一次接收、一次判断:
if r.Err != nil { … } else { use r.Value } - 千万别在 goroutine 里
panic:它不会冒泡到主流程,recover又难写难维护
多个任务并发+聚合,用 sync.WaitGroup 控制启动,用 select 收集结果
类似 Promise.all 的场景,不是靠“链式”,而是靠并发控制 + 统一等待:
- 启动 N 个 goroutine,每个往自己的带缓冲
chan Result[T]发一次结果 - 用
sync.WaitGroup确保全部启动完毕(不是等完成) - 主 goroutine 用
select循环接收 N 次,或配合context.WithTimeout设置总超时 - channel 方向性是编译期强制的:
表示“只能读”,<code>chan 表示“只能写”——写错方向,编译器立刻报错,这其实是 Go 在帮你守住边界
最容易被忽略的是 channel 的关闭时机和方向性。close 不是可选操作,它是接收方判断“任务已结束”的唯一信号;而只读/只写类型也不是注释,是编译器强制的契约。这两点错了,链式逻辑就从根上不可靠。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











