Go中异步请求处理的核心是goroutine启动并发任务、channel传递结果,配合sync.WaitGroup控制流程,避免闭包陷阱和死锁,并通过context超时与错误结构体保障可靠性。
在 Go 中实现异步请求处理,核心是用 goroutine 启动并发任务,再用 channel 传递结果或控制流程。它不是“模拟异步”,而是天然支持的轻量级并发模型——无需回调、不用 Promise,写起来更直接。
每个请求单独起一个 goroutine,避免阻塞主线程。注意:要传参(如 URL)进 goroutine,别直接用循环变量(会闭包捕获最后值)。
go func(url string) { ... }(url) 立即传值启动http.DefaultClient.Do(req) 发起请求,手动设置超时更稳妥defer resp.Body.Close()
定义一个结构体 channel(如 chan Result),每个 goroutine 执行完把结果(成功/失败、数据、错误)发进去。主 goroutine 用 for range 读取,配合 sync.WaitGroup 或 len(urls) 控制接收次数。
sync.WaitGroup:启动前 wg.Add(len(urls)),每个 goroutine 结束调 wg.Done(),主协程用 go func() { wg.Wait(); close(ch) }() 关闭 channel单个请求超时用 context.WithTimeout;整体等待超时用 select + time.After。
ctx, cancel := context.WithTimeout(context.Background(), 5*time
.Second)
select 监听 result channel 和 timeout channel,超时就提前退出以下是最小可运行骨架(省略 import):
type Result struct {
URL string
Data []byte
Err error
}
func fetchURL(ctx context.Context, url string, ch chan<- Result) {
req, := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
ch <- Result{URL: url, Err: err}
return
}
defer resp.Body.Close()
data, := io.ReadAll(resp.Body)
ch <- Result{URL: url, Data: data}
}
func main() {
urls := []string{"https://www./link/5f69e19efaba426d62faeab93c308f5c", "https://www./link/ef246753a70fce661e16668898810624"}
ch := make(chan Result, len(urls))
for _, u := range urls {
go fetchURL(context.Background(), u, ch)
}
for i := 0; i < len(urls); i++ {
r := <-ch
if r.Err != nil {
log.Printf("fail %s: %v", r.URL, r.Err)
} else {
log.Printf("success %s, len=%d", r.URL, len(r.Data))
}
}}
基本上就这些。Goroutine + Channel 不是“高级技巧”,而是 Go 处理并发请求的标准姿势——写得少、跑得稳、逻辑清晰。