Lecciones del módulo (5/5)
context.Context: cancelación y plazos
El paquete context es el estándar de facto para propagar
cancelación, plazos y valores de alcance a lo largo de la cadena de llamadas,
a través de múltiples gorutinas. Todos los paquetes estándar de "redes"
(net/http, database/sql, os/exec) aceptan un context.Context como
su primer parámetro.
La interfaz
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}Done(): canal que se cierra cuando se cancela el contexto.Err(): motivo de cancelación (context.Canceledocontext.DeadlineExceeded).Deadline(): plazo opcional.Value(): búsqueda de valores de ámbito limitado (úselo con moderación).
Las cuatro funciones básicas
ctx := context.Background() // radice: usalo in main, init, test
ctx := context.TODO() // "non so ancora": stub esplicito
ctx, cancel := context.WithCancel(parent) // cancel manuale
ctx, cancel := context.WithTimeout(parent, d) // scadenza relativa
ctx, cancel := context.WithDeadline(parent, t) // scadenza assoluta
ctx := context.WithValue(parent, key, value) // valore scope-boundTodas las formas derivadas devuelven una función cancel: llámala siempre
(idealmente con defer) para liberar recursos internos, incluso si el
El contexto ya ha expirado.
Patrón de tiempo de espera
func fetchUser(id string) (*User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
return queryUser(ctx, id) // queryUser deve rispettare ctx
}Si queryUser tarda más de 2 segundos, ctx.Done() se cierra y
la función debe abortar la operación.
Respetando el contexto: seleccione en Listo()
func slowWork(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return ctx.Err() // canceled o deadline exceeded
case <-time.After(100 * time.Millisecond):
// un tick di lavoro
}
}
}La regla: en cualquier lugar donde su función pueda bloquearse durante mucho tiempo, debe manejar ctx.Done(). De lo contrario, el contexto está "encendido" durante nada.
Convención de primer parámetro
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }
// ^^^^^^^^^^^^^^^^^^^
// SEMPRE primo, mai dentro a una struct, mai opzionalectxSIEMPRE como primer parámetro, denominadoctx.- Nunca ponga
Contextcomo campo de una estructura (excepciones muy raras). - Nunca pase
nil: usecontext.TODO()como código auxiliar.
contexto.WithValue: con moderación
type userKey struct{}
ctx := context.WithValue(ctx, userKey{}, currentUser)
// più sotto:
if u, ok := ctx.Value(userKey{}).(*User); ok { ... }Pautas:
- SÓLO para datos con alcance de solicitud (ID de solicitud, ID de seguimiento, identidad de usuario).
- NUNCA para parámetros de función: páselos normalmente.
- Claves de tipo personalizado no exportado para evitar colisiones.
Propagación a lo largo de la cadena.
func handleHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // context legato al ciclo di vita della richiesta
user, err := fetchUser(ctx, r.URL.Query().Get("id"))
if err != nil { ... }
// ...
}net/http cancela automáticamente r.Context() cuando el cliente
se desconecta o cuando el controlador regresa. Al propagarlo a todos
llamadas posteriores, todo el árbol de operaciones se cancela en
cascada: no se desperdicia trabajo después de la desconexión.
Pruébalo
Cree un contexto con un tiempo de espera de 1 segundo derivado de context.Background() y recuerde llamar a cancelar con aplazamiento.
Mostrar pista
CÓDIGOF0 devuelve CÓDIGO1; siempre llame a cancelar.
Solución disponible después de 3 intentos
Utilice <-ctx.Done() en una selección para esperar a que se cancele el contexto.
Mostrar pista
`ctx.Done()` es un canal que se cierra cuando se cancela el contexto.
Solución disponible después de 3 intentos
Por convención, ¿dónde va el parámetro ctx en la firma de una función?
func Fetch(???, url string) ([]byte, error)Resumen
context.Contextpropaga valores de cancelación, fecha límite y alcance limitado.Background()/TODO()como raíz;WithCancel/WithTimeout/WithDeadlinepara derivar.- Siempre
defer cancel()para liberar recursos. - Las funciones "largas" deben
selectenctx.Done()o pasar ctx a paquetes estándar. - Convención:
ctxcomo PRIMER parámetro, nunca dentro de una estructura. Background()0 solo para datos de ámbito de solicitud (ID de solicitud, ID de seguimiento).- En
Background()1,Background()2 se cancela al desconectarse el cliente.