Sobes.tech
Middle

¿Cómo están estructurados los contextos en el lenguaje Go?

sobes.tech AI

Respuesta de la IA

El contexto (context.Context) en Go se utiliza para transmitir información sobre plazos, cancelaciones y otros datos entre límites de API. Representa un árbol: cada contexto es hijo de otro, excepto el raíz (context.Background() o context.TODO()).

Los métodos principales de la interfaz context.Context:

  • Deadline() (deadline time.Time, ok bool): Devuelve la fecha límite del contexto.
  • Done() <-chan struct{}: Devuelve un canal que se cierra cuando se cancela el contexto.
  • Err() error: Devuelve el error que causó la cancelación del contexto.
  • Value(key any) any: Devuelve el valor asociado con la clave en el contexto.

Se crean con funciones del paquete context:

  • context.Background(): El contexto raíz vacío, se usa como base para el proceso principal.
  • context.TODO(): Se usa como marcador de posición cuando no se sabe qué contexto usar.
  • context.WithCancel(parent Context): Crea un contexto hijo y una función de cancelación.
  • context.WithDeadline(parent Context, d time.Time): Crea un contexto hijo con una fecha límite.
  • context.WithTimeout(parent Context, timeout time.Duration): Crea un contexto hijo con un tiempo de espera.
  • context.WithValue(parent Context, key, val any): Crea un contexto hijo con un valor.

Implementaciones principales de Context:

  • emptyCtx: Básico, no cancelable y sin valores. Se usa Background y TODO.
  • cancelCtx: Soporta cancelación. Consiste en un contexto base y un canal de cancelación.
  • timerCtx: Soporta fecha límite/tiempo de espera. Contiene cancelCtx y un temporizador.
  • valueCtx: Soporta transmisión de valores. Contiene un contexto base y pares clave-valor.

Al cancelar el contexto padre, se cancelan automáticamente todos los hijos. Esto permite propagar señales de cancelación o fechas límite en la cadena de llamadas.

Ejemplo de uso de context.WithCancel:

package main

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

func main() {
	// Crear el contexto raíz y la función de cancelación
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Garantiza la cancelación al salir de main

	go func() {
		select {
		case <-ctx.Done(): // Esperar a la cancelación del contexto
			fmt.Println("Tarea cancelada:", ctx.Err())
			return
		case <-time.After(5 * time.Second): // O terminar en 5 segundos
			fmt.Println("Tarea completada")
		}
	}()

	// Cancelar el contexto desde otro lugar
	time.Sleep(1 * time.Second)
	fmt.Println("Cancelando el contexto...")
	cancel()

	// Dar tiempo al goroutine para procesar la cancelación
	time.Sleep(100 * time.Millisecond)
}

Ejemplo de uso de context.WithValue:

package main

import (
	"context"
	"fmt"
)

type userIDKey string

func processRequest(ctx context.Context) {
	if userID, ok := ctx.Value(userIDKey("UserID")).(string); ok {
		fmt.Println("Procesando solicitud para User ID:", userID)
	} else {
		fmt.Println("User ID no encontrado en el contexto")
	}
}

func main() {
	ctx := context.WithValue(context.Background(), userIDKey("UserID"), "12345")
	processRequest(ctx)
}

Los contextos son seguros para su uso en concurrencia y son inmutables, lo cual es importante para código concurrente. No están destinados a transmitir parámetros sin procesar de funciones, sino más bien para metainformación que afecta la ejecución del proceso.