Go by Example: Timeouts

Los tiempos de espera (Timeouts) son importantes para programas que se conectan a recursos externos o que de otra manera necesitan limitar el tiempo de ejecución. Implementar tiempos de espera en Go es fácil y elegante gracias a los canales y select.

package main
import (
    "fmt"
    "time"
)
func main() {

Para nuestro ejemplo, supongamos que estamos ejecutando una llamada externa que devuelve su resultado en un canal c1 después de 2s. Observa que el canal está bufferizado, por lo que el envío en la goroutine es no bloqueante. Este es un patrón común para prevenir fugas de goroutines en caso de que el canal nunca se lea.

    c1 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c1 <- "result 1"
    }()

Aquí está el select implementando un tiempo de espera. res := <-c1 espera el resultado y <-time.After espera un valor que se envíe después del tiempo de espera de 1s. Dado que select procede con la primera recepción que esté lista, tomaremos el caso de tiempo de espera si la operación tarda más de 1s permitido.

    select {
    case res := <-c1:
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        fmt.Println("timeout 1")
    }

Si permitimos un tiempo de espera más largo de 3s, entonces la recepción desde c2 tendrá éxito y imprimiremos el resultado.

    c2 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "result 2"
    }()
    select {
    case res := <-c2:
        fmt.Println(res)
    case <-time.After(3 * time.Second):
        fmt.Println("timeout 2")
    }
}

Ejecutar este programa muestra que la primera operación se agota por tiempo y la segunda tiene éxito.

$ go run timeouts.go 
timeout 1
result 2

Siguiente ejemplo: Non-Blocking Channel Operations.