The most common use case is when developers are trying to send multiple HTTP requests to different endpoints. Let’s say we want to send to 10 endpoints. The average time to execute each endpoint is 100ms. We will get 10 endpoints x 100ms = 1000ms if we do it sequentially. This is a raw calculation. It does not include how we parse the response yet. Let’s solve this case properly.
packagemainimport("encoding/json""errors""fmt""io""log""net/http""time""golang.org/x/sync/errgroup")var(errProducts=errors.New("failed to retrieve products"))typeproductstruct{IDuint`json:"id"`Titlestring`json:"title"`Pricefloat64`json:"price"`}typeProductRepositorystruct{Client*http.Client}func(pProductRepository)Products()([]product,error){products:=[]product{}fori:=1;i<=10;i++{resp,err:=p.Client.Get(fmt.Sprintf("https://dummyjson.com/products/%d",i))iferr!=nil{log.Println(err)returnnil,errProducts}body,err:=io.ReadAll(resp.Body)iferr!=nil{log.Println(err)returnnil,errProducts}product:=product{}iferr:=json.Unmarshal(body,&product);err!=nil{log.Println(err)returnnil,errProducts}products=append(products,product)}returnproducts,nil}funcmain(){productService:=ProductRepository{Client:http.DefaultClient,}start:=time.Now()products,err:=productService.Products()iferr!=nil{log.Panic(err)}log.Println(products)log.Println(time.Since(start))}
It looks like our code is clean. That code was executed for around 3.4s. Take a look at the code carefully. What is the bug?
That code successfully reduces the execution time from around 3.4s to around 840ms. Here are the points:
Use errgroup.Group to group those executions and catch the error later. It is better than using go func() {}() because the errgroup.Group made us able to catch the error cleanly.
Use make([]product, n) and products[i-1] = product instead of []product{} and products = append(products, product). Use array instead of slice. So we get indexes and can assign value to them.
Using a single *http.Client is no problem. We don't need many HTTP clients (eg; []*http.Client) to handle many requests because a single *http.Client reuses TCP connections efficiently via connection pooling. Creating multiple clients may lead to unnecessary connection duplications, increasing system resource usage.
The code above is an example of using errgroup.Group. You can use the same technique for gRPC, GraphQL, etc.