context.WithCancelCause în Go

Ca WithCancel, dar cancel primește motivul anulării; context.Cause(ctx) returnează acel motiv.

Sintaxă
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)
type CancelCauseFunc func(cause error)
func Cause(c Context) error

Ce face context.WithCancelCause

Cu WithCancel, după anulare ctx.Err() spune doar context canceled, fără motiv. WithCancelCause returnează o funcție cancel(err) care primește o eroare: motivul. ctx.Err() rămâne context.Canceled (pentru compatibilitate), dar context.Cause(ctx) returnează eroarea dată de tine.

context.Cause funcționează pe orice context: pentru un context activ returnează nil, pentru unul anulat fără motiv returnează același lucru ca Err(), iar pentru copiii unui context anulat cu motiv returnează motivul părintelui. cancel(nil) pune motivul context.Canceled.

Parametri

Parametru Descriere
parent Contextul părinte.

Ce returnează

Contextul și funcția cancel(cause error). Cause returnează motivul (error) sau nil.

Exemple

Exemplul 1: Motivul anulării

package main

import (
	"context"
	"errors"
	"fmt"
)

var ErrStocEpuizat = errors.New("stoc epuizat")

func main() {
	ctx, cancel := context.WithCancelCause(context.Background())
	fmt.Println("Activ: Err =", ctx.Err(), "Cause =", context.Cause(ctx))

	cancel(ErrStocEpuizat)
	fmt.Println("Err:  ", ctx.Err())
	fmt.Println("Cause:", context.Cause(ctx))
	fmt.Println("Este stoc epuizat:", errors.Is(context.Cause(ctx), ErrStocEpuizat))

	copil, cancelCopil := context.WithCancel(ctx)
	defer cancelCopil()
	fmt.Println("Cause la copil:", context.Cause(copil))
}

Rezultat

Activ: Err = <nil> Cause = <nil>
Err:   context canceled
Cause: stoc epuizat
Este stoc epuizat: true
Cause la copil: stoc epuizat

Exemplul 2: Primul lucrător care eșuează oprește grupul

Lucrătorul care găsește o eroare anulează contextul cu ea; ceilalți se opresc, iar main află motivul real.

package main

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

func valideaza(ctx context.Context, cancel context.CancelCauseFunc, fisier string) {
	if fisier == "date.csv" {
		cancel(fmt.Errorf("%s: linia 7 are un CNP invalid", fisier))
		return
	}
	select {
	case <-time.After(time.Second):
	case <-ctx.Done():
	}
}

func main() {
	ctx, cancel := context.WithCancelCause(context.Background())
	defer cancel(nil)

	var wg sync.WaitGroup
	for _, f := range []string{"clienti.csv", "date.csv", "produse.csv"} {
		wg.Go(func() { valideaza(ctx, cancel, f) })
	}
	wg.Wait()
	fmt.Println("Err:  ", ctx.Err())
	fmt.Println("Motiv:", context.Cause(ctx))
}

Rezultat

Err:   context canceled
Motiv: date.csv: linia 7 are un CNP invalid

Greșeli frecvente

  • Te uiți doar la ctx.Err() și te miri că motivul lipsește. Err() rămâne context.Canceled; motivul se citește cu context.Cause(ctx).

Comenzi înrudite

  • context.WithCancel: Creează un context copil și o funcție cancel; apelul cancel() anunță toate goroutine-urile care îl folosesc să se oprească.
  • context.WithTimeoutCause: Ca WithTimeout și WithDeadline, dar la expirare context.Cause(ctx) returnează eroarea ta, nu doar DeadlineExceeded.
  • errors.Is: Verifică dacă eroarea err este target sau o conține, căutând prin toate erorile împachetate cu %w sau Join.