go不内置future/promise,应使用goroutine+channel原生模式:超时select、返回chan t的函数;避免promise抽象、重复close/send、手动取消,改用context.context。

Go 里没有内置 Future/Promise,别硬套 JS 那套写法
Go 的并发模型基于 goroutine 和 channel,不是靠“等待一个未来值”来组织逻辑。强行用结构体封装 chan 模拟 Promise.then(),只会让代码更难读、更难测、更容易死锁。
真正该用的,是 Go 原生支持的模式:带超时的 select + chan,或封装成返回 chan T 的函数(即“异步任务工厂”)。
- 不要定义
type Promise[T any] struct { c chan T }这类抽象——它不解决实际问题,还遮蔽了 channel 的流向语义 - 不要在 goroutine 里反复
close(c)或重复发送——panic: send on closed channel是最常见错误之一 - 如果需要“取消”,优先用
context.Context控制生命周期,而不是靠额外 channel 通知“别等了”
用 func() 实现轻量级异步任务
这是最贴近 Future 语义、又完全符合 Go 习惯的写法:函数立即返回一个只读 channel,调用方按需接收结果,底层自动启 goroutine 执行耗时操作。
示例:从 HTTP 获取 JSON 数据
立即学习“go语言免费学习笔记(深入)”;
func fetchUser(id int) <-chan *User {
ch := make(chan *User, 1)
go func() {
defer close(ch)
resp, err := http.Get("https://api.example.com/users/" + strconv.Itoa(id))
if err != nil {
return
}
defer resp.Body.Close()
var u User
json.NewDecoder(resp.Body).Decode(&u)
ch <- &u
}()
return ch
}- 必须用
make(chan T, 1)缓冲 1,否则 goroutine 可能阻塞在发送端(尤其当调用方还没就退出时) - 必须
defer close(ch),否则接收方永远阻塞;但仅在 goroutine 内 close,不能由外部 close - 如果函数要支持取消,加一个
ctx context.Context参数,并在 HTTP 调用和 channel 发送前检查ctx.Done()
多个异步任务合并?用 select + for range 处理 channel slice
JS 里的 Promise.all() 在 Go 里对应的是“等待多个 channel 全部发完”,但 Go 没有原生批量 select,得自己收拢结果。
常见错误:直接把一堆 写死在 select 里——数量固定还行,动态数量就只能循环建 goroutine + 汇总 channel。
- 推荐做法:每个异步任务返回
,启动一个 collector goroutine,用 <code>for range从每个 channel 读取(注意 channel 关闭后range自动退出) - 别用
reflect.Select动态 select——性能差、可读性崩坏、调试困难 - 如果要保序(比如第 3 个请求结果必须排第 3),就在
Result结构体里带上 index 字段,别依赖接收顺序
为什么不用第三方 Promise 库?
目前有几个开源库试图提供 Promise[T] 类型(如 gofuture、go-promise),但它们都绕不开三个根本矛盾:
- Go 的 channel 是一等公民,而 Promise 库把 channel 包一层后,反而要额外处理
then/catch的嵌套、状态机、错误透传——这些在 Go 里用if err != nil+return更直白 - 所有 Promise 库的
Wait()方法本质都是,但用户容易误以为它是“同步阻塞”,忽略它仍可能 panic(如 channel 已 close 却没判空) - 和
context集成差:Promise 库基本不支持 cancel-aware 的 resolve/reject,而真实服务端场景中,超时和取消比“成功/失败”更常发生
真正复杂的异步编排(比如依赖图、重试策略、熔断),应该用专门的调度库(如 asynq、temporal),而不是在语言层模拟 Promise 语义。










