Sobes.tech
Middle

Cuéntame sobre los casos de uso del contexto en Go.

sobes.tech AI

Respuesta de la IA

El contexto en Go se utiliza para gestionar el ciclo de vida de las solicitudes y controlar la ejecución de las goroutines. Los casos principales de uso:

  • Cancelación: Permite cancelar la cadena de goroutines relacionada con una solicitud específica. Por ejemplo, al cerrar la conexión o por timeout.
  • Timeout: Establece un plazo límite para la operación. Si la operación no termina en ese plazo, el contexto se cancela. Esto evita esperas infinitas.
  • Transferencia de datos específicos de la solicitud: Permite pasar datos relacionados con una solicitud concreta (como ID de usuario, identificador de trazas) a través de la cadena de llamadas sin cambiar sus firmas.

Ejemplos de uso:

  1. Cancelación por señal:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func worker(ctx context.Context, id int) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Printf("Trabajador %d interrumpido\n", id)
    			return
    		default:
    			fmt.Printf("Trabajador %d trabajando...\n", id)
    			time.Sleep(500 * time.Millisecond)
    		}
    	}
    }
    
    func main() {
    	ctx, cancel := context.WithCancel(context.Background())
    	defer cancel() // Garantiza la cancelación
    
    	go worker(ctx, 1)
    	go worker(ctx, 2)
    
    	time.Sleep(2 * time.Second)
    	cancel() // Cancela el contexto
    
    	time.Sleep(500 * time.Millisecond) // Da tiempo a que las goroutines terminen
    }
    
  2. Timeout:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func longOperation(ctx context.Context) error {
    	select {
    	case <-time.After(3 * time.Second): // Simulación de una operación larga
    		fmt.Println("Operación completada con éxito")
    		return nil
    	case <-ctx.Done():
    		fmt.Println("Operación cancelada por el contexto:", ctx.Err())
    		return ctx.Err()
    	}
    }
    
    func main() {
    	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    	defer cancel()
    
    	err := longOperation(ctx)
    	if err != nil {
    		fmt.Println("Error:", err)
    	}
    }
    
  3. Transferencia de datos:

    package main
    
    import (
    	"context"
    	"fmt"
    )
    
    type UserKey string
    
    const userIDKey UserKey = "userID"
    
    func processRequest(ctx context.Context) {
    	userID := ctx.Value(userIDKey).(int)
    	fmt.Printf("Procesando solicitud para el usuario ID: %d\n", userID)
    	// Procesamiento adicional de la solicitud usando userID
    }
    
    func main() {
    	ctx := context.WithValue(context.Background(), userIDKey, 123)
    	processRequest(ctx)
    }
    

Es importante usar context.Background() o context.TODO() como contexto raíz y pasarlo hacia abajo en la cadena de llamadas. No se debe almacenar el contexto en estructuras de datos si no está directamente relacionado con el ciclo de vida de esa estructura.