Implementing Go Timeouts with Channels

Beginner

This tutorial is from open-source community. Access the source code

Introduction

The purpose of this lab is to implement timeouts in Go using channels and select.

Timeouts

When programs connect to external resources or need to bound execution time, timeouts are important. The lab is to implement timeouts in Go using channels and select.

  • Implement timeouts in Go using channels and select.
  • Use a buffered channel to prevent goroutine leaks in case the channel is never read.
  • Use time.After to await a value to be sent after the timeout.
  • Use select to proceed with the first receive that's ready.
## Running this program shows the first operation timing
## out and the second succeeding.
$ go run timeouts.go
timeout 1
result 2

There is the full code below:

// _Timeouts_ are important for programs that connect to
// external resources or that otherwise need to bound
// execution time. Implementing timeouts in Go is easy and
// elegant thanks to channels and `select`.

package main

import (
    "fmt"
    "time"
)

func main() {

    // For our example, suppose we're executing an external
    // call that returns its result on a channel `c1`
    // after 2s. Note that the channel is buffered, so the
    // send in the goroutine is nonblocking. This is a
    // common pattern to prevent goroutine leaks in case the
    // channel is never read.
    c1 := make(chan string, 1)
    go func() {
        time.Sleep(2 * time.Second)
        c1 <- "result 1"
    }()

    // Here's the `select` implementing a timeout.
    // `res := <-c1` awaits the result and `<-time.After`
    // awaits a value to be sent after the timeout of
    // 1s. Since `select` proceeds with the first
    // receive that's ready, we'll take the timeout case
    // if the operation takes more than the allowed 1s.
    select {
    case res := <-c1:
        fmt.Println(res)
    case <-time.After(1 * time.Second):
        fmt.Println("timeout 1")
    }

    // If we allow a longer timeout of 3s, then the receive
    // from `c2` will succeed and we'll print the result.
    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")
    }
}

Summary

In this lab, we learned how to implement timeouts in Go using channels and select. We used a buffered channel to prevent goroutine leaks in case the channel is never read, and time.After to await a value to be sent after the timeout. We also used select to proceed with the first receive that's ready.