Go中异步请求处理的核心是goroutine启动并发任务、channel传递结果,配合sync.WaitGroup控制流程,避免闭包陷阱和死锁,并通过context超时与错误结构体保障可靠性。

在 Go 中实现异步请求处理,核心是用 goroutine 启动并发任务,再用 channel 传递结果或控制流程。它不是“模拟异步”,而是天然支持的轻量级并发模型——无需回调、不用 Promise,写起来更直接。
用 goroutine 快速发起多个 HTTP 请求
每个请求单独起一个 goroutine,避免阻塞主线程。注意:要传参(如 URL)进 goroutine,别直接用循环变量(会闭包捕获最后值)。
- 用
go func(url string) { ... }(url)立即传值启动 - 用
http.DefaultClient.Do(req)发起请求,手动设置超时更稳妥 - 别忘了关闭响应体:
defer resp.Body.Close()
用 channel 收集结果并等待全部完成
定义一个结构体 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 - 避免死锁:确保所有 goroutine 都会发值,且主 goroutine 不漏读
加超时和错误处理,让异步更可靠
单个请求超时用 context.WithTimeout;整体等待超时用 select + time.After。
立即学习“go语言免费学习笔记(深入)”;
- 每个请求构造带 timeout 的 context:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - 主流程用
select监听 result channel 和 timeout channel,超时就提前退出 - 错误不 panic:把 error 作为字段放进 Result 结构体,统一由调用方判断
简单示例:并发抓取多个 URL
以下是最小可运行骨架(省略 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.php.cn/link/5f69e19efaba426d62faeab93c308f5c", "https://www.php.cn/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 处理并发请求的标准姿势——写得少、跑得稳、逻辑清晰。










