如何避免Go语言中API请求相关函数的代码重复?
用泛型实现通用并发请求函数,消除代码重复
针对你遇到的并发请求函数逻辑重复、缺少自定义结构体支持的问题,用Go泛型可以完美解决——实现一个通用的底层并发请求函数,让所有特定类型的并发请求都基于它封装,彻底消除重复代码,同时轻松扩展自定义结构体的支持。
第一步:定义通用并发请求底层函数
这个函数接收请求输入列表和单个请求的处理函数,利用泛型T适配任意返回类型,通过通道保证结果顺序和并发安全:
import "sync" // GenericConcurrentRequests 通用并发请求处理函数 // 参数: // inputs: 请求输入列表(可以是URL字符串,也可以是自定义的http.Request对象等) // singleReqFunc: 单个请求的处理函数,接收单个输入,返回泛型结果T和错误 // 返回:按输入顺序排列的结果切片、对应位置的错误切片 func GenericConcurrentRequests[T any, I any](inputs []I, singleReqFunc func(I) (T, error)) ([]T, []error) { resultChan := make(chan struct { idx int value T err error }, len(inputs)) var wg sync.WaitGroup wg.Add(len(inputs)) // 启动goroutine处理每个请求 for i, input := range inputs { go func(idx int, in I) { defer wg.Done() val, err := singleReqFunc(in) resultChan <- struct { idx int value T err error }{idx, val, err} }(i, input) } // 所有请求完成后关闭通道 go func() { wg.Wait() close(resultChan) }() // 初始化结果容器,按原顺序填充 results := make([]T, len(inputs)) errs := make([]error, len(inputs)) for res := range resultChan { results[res.idx] = res.value errs[res.idx] = res.err } return results, errs }
第二步:基于通用函数封装特定类型的并发请求
原来的GetJsonAsMapMultiple、GetJsonAsMapArrayMultiple,以及新增的自定义结构体并发请求,都可以一行代码实现:
假设你已有以下单请求函数:
// 单请求返回Map func GetJsonAsMap(url string) (map[string]interface{}, error) { // 原有的单请求逻辑:发送请求、解析JSON为Map } // 单请求返回Map数组 func GetJsonAsMapArray(url string) ([]map[string]interface{}, error) { // 原有的单请求逻辑:发送请求、解析JSON为Map数组 } // 单请求填充自定义结构体 func GetJsonAsStruct[T any](url string) (T, error) { // 原有的单请求逻辑:发送请求、解析JSON为自定义结构体 }
现在封装并发版本:
// Map类型的并发请求 func GetJsonAsMapMultiple(urls []string) ([]map[string]interface{}, []error) { return GenericConcurrentRequests[map[string]interface{}, string](urls, GetJsonAsMap) } // Map数组类型的并发请求 func GetJsonAsMapArrayMultiple(urls []string) ([][]map[string]interface{}, []error) { return GenericConcurrentRequests[[]map[string]interface{}, string](urls, GetJsonAsMapArray) } // 自定义结构体的并发请求 func GetJsonAsStructMultiple[T any](urls []string) ([]T, []error) { return GenericConcurrentRequests[T, string](urls, GetJsonAsStruct[T]) }
扩展支持自定义请求(如带Header/Body的HTTP请求)
如果需要支持更复杂的自定义请求(比如带请求头、请求体的http.Request),只需要调整输入类型即可:
// 单请求处理自定义http.Request,返回结构体 func GetJsonAsStructWithRequest[T any](req *http.Request) (T, error) { // 原有的单请求逻辑:发送自定义请求、解析JSON为结构体 } // 自定义请求的并发版本 func GetJsonAsStructWithRequestMultiple[T any](requests []*http.Request) ([]T, []error) { return GenericConcurrentRequests[T, *http.Request](requests, GetJsonAsStructWithRequest[T]) }
方案优势
- 彻底消除代码重复:所有并发请求的核心逻辑(goroutine管理、结果收集、顺序保证)都集中在
GenericConcurrentRequests中,上层只需关注单请求的业务逻辑。 - 灵活扩展:新增任何类型的并发请求,只需封装一行代码,无需重复编写并发逻辑。
- 统一维护:后续如果需要添加超时控制、重试机制、请求限流等功能,只需修改通用函数一处即可,所有上层函数自动受益。
内容的提问来源于stack exchange,提问作者vipaware
相关产品推荐
相关产品推荐

