context.Context in Go: Timeouts and Cancellation
A context.Context carries a cancellation signal and an optional deadline through a chain of function calls. When a user closes the browser tab, every database query and HTTP call made for that request can stop.
Every request already has one
func handleSearch(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // cancelled when the client disconnects
results, err := search(ctx, r.URL.Query().Get("q"))
if err != nil {
if errors.Is(err, context.Canceled) {
return // client gave up; nothing to send
}
http.Error(w, "search failed", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(results)
}
Add a timeout
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel() // always call cancel to release resources
row := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = $1", id)
Pass it down
func search(ctx context.Context, q string) ([]Result, error) {
req, _ := http.NewRequestWithContext(ctx, "GET", searchURL(q), nil)
resp, err := http.DefaultClient.Do(req) // aborts when ctx is cancelled
if err != nil {
return nil, err
}
defer resp.Body.Close()
// ...
}
Respect cancellation in your own loops
for _, item := range items {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
process(item)
}
Rules
- Context is the first parameter, named
ctx. - Don't store contexts in structs; pass them explicitly.
- Never pass
nil; usecontext.Background()at the top level orcontext.TODO()while refactoring. - Use
context.WithValueonly for request-scoped data like trace IDs, not for optional function parameters.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.