2026/9/10
Goroutines with the errgroup package's thumbnail

Goroutines with the errgroup package

  • Go

Package errgroup has a Go method that takes a function and calls it inside a new goroutine. It’s so convenient when you use goroutines and want to handle their errors in one place. Maybe I’ll write such code more often than plain goroutines.

package mainimport (	"encoding/json"	"fmt"	"log"	"net/http"	"golang.org/x/sync/errgroup")func main() {	userID := 1	user, err := fetchUserByID(userID)	if err != nil {		log.Fatalf("failed to fetch user: %v", err)	}	var posts []Post	var todos []Todo	errGroup := new(errgroup.Group)	errGroup.Go(func() error {		var err error		posts, err = fetchPostsByUserID(user.ID)		return err	})	errGroup.Go(func() error {		var err error		todos, err = fetchTodosByUserID(user.ID)		return err	})	if err := errGroup.Wait(); err != nil {		log.Fatalf("error fetching posts or todos: %v", err)	}	fmt.Println("time taken:", elapsed)	fmt.Printf("user: %+v\n", user)	fmt.Printf("posts: %+v\n", posts)	fmt.Printf("todos: %+v\n", todos)}// Structs for JSONPlaceholder API responsestype User struct {	ID   int    `json:"id"`	Name string `json:"name"`}type Post struct {	ID     int    `json:"id"`	UserID int    `json:"userId"`	Title  string `json:"title"`	Body   string `json:"body"`}type Todo struct {	ID          int    `json:"id"`	UserID      int    `json:"userId"`	Title       string `json:"title"`	IsCompleted bool   `json:"completed"`}// Fetch functions for JSONPlaceholder APIconst baseURL = "https://jsonplaceholder.typicode.com"func fetchUserByID(id int) (User, error) {	url := fmt.Sprintf("%s/users/%d", baseURL, id)	return fetchByURL[User](url)}func fetchTodosByUserID(userID int) ([]Todo, error) {	url := fmt.Sprintf("%s/users/%d/todos", baseURL, userID)	return fetchByURL[[]Todo](url)}func fetchPostsByUserID(userID int) ([]Post, error) {	url := fmt.Sprintf("%s/users/%d/posts", baseURL, userID)	return fetchByURL[[]Post](url)}func fetchByURL[T any](url string) (T, error) {	res, err := http.Get(url)	if err != nil {		var zero T		return zero, err	}	defer res.Body.Close()	if res.StatusCode != http.StatusOK {		var zero T		return zero, fmt.Errorf("failed to fetch data from %s: status code %d", url, res.StatusCode)	}	var value T	if err := json.NewDecoder(res.Body).Decode(&value); err != nil {		var zero T		return zero, err	}	return value, nil}