SyntaxStudy
Sign Up
Go Context for Cancellation and Timeouts
Go Beginner 1 min read

Context for Cancellation and Timeouts

The `context` package provides a standard way to carry deadlines, cancellation signals, and request-scoped values across goroutines and API boundaries. Every function that may block, perform I/O, or launch goroutines should accept a `context.Context` as its first parameter. This makes cancellation and timeouts explicit and composable. `context.WithCancel` returns a child context and a cancel function. When you call the cancel function, all goroutines that are watching `ctx.Done()` receive a signal and should stop their work and return. `context.WithTimeout` and `context.WithDeadline` automatically cancel the context after a duration or at a specific time, making them ideal for bounding the execution time of operations. A critical rule is that the function that creates a context with a cancel function is responsible for calling cancel to release resources, regardless of whether the context is actually cancelled by the parent. The idiomatic pattern is `ctx, cancel := context.WithTimeout(parent, d); defer cancel()`. Failing to call cancel leaks the goroutines and timers associated with the context.
Example
package main

import (
    "context"
    "fmt"
    "time"
)

func doWork(ctx context.Context, id int) error {
    select {
    case <-time.After(200 * time.Millisecond):
        fmt.Printf("worker %d finished\n", id)
        return nil
    case <-ctx.Done():
        fmt.Printf("worker %d cancelled: %v\n", id, ctx.Err())
        return ctx.Err()
    }
}

func fetchData(ctx context.Context, url string) (string, error) {
    // simulate a slow network request
    select {
    case <-time.After(500 * time.Millisecond):
        return "data from " + url, nil
    case <-ctx.Done():
        return "", fmt.Errorf("fetch %s: %w", url, ctx.Err())
    }
}

func main() {
    // WithCancel — manual cancellation
    ctx, cancel := context.WithCancel(context.Background())
    go func() {
        time.Sleep(100 * time.Millisecond)
        cancel() // cancel after 100ms
    }()
    doWork(ctx, 1)

    // WithTimeout — automatic cancellation
    ctx2, cancel2 := context.WithTimeout(context.Background(), 300*time.Millisecond)
    defer cancel2()
    data, err := fetchData(ctx2, "https://example.com")
    if err != nil {
        fmt.Println("error:", err)
    } else {
        fmt.Println("received:", data)
    }
}