Frontend Labs
⋯

29. Concurrency patterns: fan-in/fan-out, pipelines, rate limiting, errgroup

29.1 Fan-out / fan-in

Distribute work across multiple goroutines (fan-out), then merge their results back into one stream (fan-in):

func fanIn(channels ...<-chan int) <-chan int {
	out := make(chan int)
	var wg sync.WaitGroup
	wg.Add(len(channels))
	for _, c := range channels {
		go func(c <-chan int) {
			defer wg.Done()
			for v := range c {
				out <- v
			}
		}(c)
	}
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

29.2 Rate limiting with time.Ticker or golang.org/x/time/rate

limiter := rate.NewLimiter(rate.Limit(10), 1) // 10 events/sec, burst 1
for _, req := range requests {
	if err := limiter.Wait(ctx); err != nil {
		break // ctx cancelled
	}
	send(req)
}

29.3 errgroup: WaitGroup that also propagates the first error

import "golang.org/x/sync/errgroup"

g, ctx := errgroup.WithContext(ctx)
for _, url := range urls {
	url := url
	g.Go(func() error {
		return fetch(ctx, url)
	})
}
if err := g.Wait(); err != nil {
	return err // the first error any goroutine returned
}

errgroup.WithContext also cancels the shared ctx the moment any goroutine returns a non-nil error — so a failing fetch stops the others from doing pointless work, without you wiring that cancellation by hand.

29.4 Real-world example

A service that fans out to five downstream APIs and needs "all succeed or report which failed, and don't wait for slow ones once one has failed" is errgroup in five lines — this exact pattern replaces pages of manual WaitGroup + mutex + error-slice bookkeeping.

Applied in the series: the HTTPS guide's resilience lab implements rate limiting, retries with jitter and a budget, and a circuit breaker.


Generated from real-life-golang-guide.md · 8 Oct 2026 16:36