
本文介绍如何在 go 中借鉴 javascript promise 思想,利用 sync.waitgroup 替代通道实现轻量、无阻塞的异步任务封装,支持链式回调(then),避免 channel 缓冲与 select 复杂性,提升执行效率。
本文介绍如何在 go 中借鉴 javascript promise 思想,利用 sync.waitgroup 替代通道实现轻量、无阻塞的异步任务封装,支持链式回调(then),避免 channel 缓冲与 select 复杂性,提升执行效率。
在 Go 中直接用 chan string 和 chan error 模拟 Promise 虽然直观,但存在明显缺陷:需手动管理缓冲容量、调用方必须显式 select 监听多个 channel、无法自然表达“完成即回调”的语义,且易因未读取导致 goroutine 泄漏。更优解是采用同步原语抽象状态——sync.WaitGroup 正是为此而生。
以下是一个生产就绪的 Promise 实现:
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
package main
import (
"fmt"
"sync"
"time"
)
type Promise struct {
wg sync.WaitGroup
res string
err error
}
// NewPromise 接收一个同步函数,立即启动 goroutine 执行,并返回 Promise 实例
// 调用方无需等待,可立刻继续执行其他逻辑
func NewPromise(f func() (string, error)) *Promise {
p := &Promise{}
p.wg.Add(1)
go func() {
defer p.wg.Done()
p.res, p.err = f()
}()
return p
}
// Then 注册成功与失败的回调函数,异步触发(非阻塞主流程)
// 回调本身也在新 goroutine 中执行,确保不拖慢等待逻辑
func (p *Promise) Then(onSuccess func(string), onError func(error)) {
go func() {
p.wg.Wait() // 阻塞在此 goroutine,不影响主线程
if p.err != nil {
onError(p.err)
} else {
onSuccess(p.res)
}
}()
}
// Await 提供同步等待能力(可选),适用于需要严格顺序的场景
func (p *Promise) Await() (string, error) {
p.wg.Wait()
return p.res, p.err
}
func main() {
// ✅ 1. 立即返回 Promise,不阻塞
p := NewPromise(func() (string, error) {
time.Sleep(2 * time.Second)
return "Hello from Promise!", nil
})
// ✅ 2. 主 goroutine 继续执行其他任务(非阻塞)
fmt.Println("Doing other work while promise runs...")
time.Sleep(500 * time.Millisecond)
fmt.Println("Still working...")
// ✅ 3. 注册回调 —— 当 Promise 完成时自动触发(无需轮询或 select)
p.Then(
func(res string) { fmt.Printf("✅ Success: %s\n", res) },
func(err error) { fmt.Printf("❌ Error: %v\n", err) },
)
// 可选:若需同步获取结果,调用 Await(会阻塞当前 goroutine)
// res, err := p.Await()
// fmt.Println("Final result:", res, err)
// 为确保回调 goroutine 有时间执行,此处小延时(实际项目中应使用信号机制如 done chan)
time.Sleep(2500 * time.Millisecond)
}
关键设计优势说明:
- 零 channel 开销:避免了 make(chan T, 1) 的内存分配与调度开销,WaitGroup 是更底层、更轻量的同步原语;
- 天然防泄漏:wg.Wait() 保证仅在任务完成后才触发回调,无需担心 channel 未消费;
- 清晰的责任分离:NewPromise 负责启动异步任务,Then 负责声明式注册响应逻辑,语义贴近 JS Promise .then();
- 线程安全:res/err 字段仅由单个 goroutine 写入,Then 中只读,无需额外 mutex;
- 可扩展性强:可轻松添加 Catch、Finally、All(批量 Promise)等方法,只需复用 wg.Wait() 模式。
⚠️ 注意:Then 内部启动新 goroutine 是为了不阻塞调用方,但若需严格控制并发(如限制回调并发数),应在上层加限流;另外,main 中的 time.Sleep 仅为演示,在真实服务中应通过 context.Context 或 sync.WaitGroup 协调主程序生命周期,避免竞态退出。










