Go por Exemplo: Timeouts

Timeouts são importantes para programas que se conectam à recursos externos ou que precisam limitar de outra forma o tempo de execução. Implementar timeouts no Go é fácil e elegante, graças aos canais e ao select.

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

Para nosso exemplo, suponhamos que iremos executar uma chamada externa que retorna seu resultado no canal c1 depois de 2s.

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

Aqui temos o select implementando o timeout. res := <-c1 espera o resultado e <-Time.After aguarda o valor a ser enviado depois do timeout de 1s. Desde que o select proceda com o primeiro recebimento pronto, nós pegaremos o timeout caso a operação leve mais do que o 1s permitido.

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

Se nós permitimos um timeout maior do que 3s, então o recebimento do c2 terá sucesso e imprimiremos o resultado.

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

A execução deste programa mostra a primeira operação de limite de tempo e o segundo sucedendo.

$ go run timeouts.go
timeout 1
resultado 2

Usar o select timeout padrão requer comunicação dos resultados através de canais. Isso é uma boa idéia em geral, pois outros recursos importantes do Go são baseados em canais e no select. Nós iremos olhar dois exemplos disso a seguir: timers e tickers.