Context
context.Context carries cancellation signals, deadlines, and small amounts of request-scoped data down a call chain — it’s the standard way to say “stop what you’re doing” across goroutine boundaries.
Cancellation
Section titled “Cancellation”ctx, cancel := context.WithCancel(context.Background())defer cancel()
go func() { select { case <-ctx.Done(): fmt.Println("cancelled:", ctx.Err()) }}()
cancel() // triggers ctx.Done()Timeouts and deadlines
Section titled “Timeouts and deadlines”ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)resp, err := http.DefaultClient.Do(req)If the request takes longer than 5 seconds, the context cancels it and Do returns an error — no manual timers needed.
The convention: ctx first, always cancel
Section titled “The convention: ctx first, always cancel”ctxis always the first parameter, by convention:func DoWork(ctx context.Context, arg string).- Every
WithCancel/WithTimeout/WithDeadlinereturns acancelfunction — alwaysdefer cancel()even if the context finishes on its own, to release its resources immediately. - Never store a
Contextinside a struct; pass it explicitly through the call chain instead.
Request-scoped values
Section titled “Request-scoped values”type key string
ctx := context.WithValue(context.Background(), key("requestID"), "abc-123")
// later, deep in the call stackif id, ok := ctx.Value(key("requestID")).(string); ok { log.Println("request:", id)}Use this sparingly — for cross-cutting metadata like request IDs or auth claims, not for passing regular function arguments.