diff --git a/examples.txt b/examples.txt index c2446fb..896170b 100644 --- a/examples.txt +++ b/examples.txt @@ -32,7 +32,7 @@ Channels # Channel Directions # Synchronization Select -# Timeouts +Timeouts # Scatter Gather # Rate Limiting # Worker Pools diff --git a/examples/timeouts/timeouts.go b/examples/timeouts/timeouts.go index fda81b9..96773dc 100644 --- a/examples/timeouts/timeouts.go +++ b/examples/timeouts/timeouts.go @@ -1,25 +1,50 @@ +// _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 "time" import "fmt" func main() { - c := make(chan string) - d := make(chan bool, 1) + // For our example, suppose we're execting an external + // call that returns its result on a channel `c1` + // after 2s. + c1 := make(chan string) go func() { - time.Sleep(time.Millisecond * 1500) - c <- "ready" + time.Sleep(time.Second * 2) + 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 1000ms. + select { + case res := <-c1: + fmt.Println(res) + case <-time.After(time.Second * 1): + 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) go func() { - select { - case msg := <-c: - fmt.Println(msg) - case <-time.After(time.Millisecond * 1000): - fmt.Println("timeout") - } - d <- true + time.Sleep(time.Second * 2) + c2 <- "result 2" }() - <-d + select { + case res := <-c2: + fmt.Println(res) + case <-time.After(time.Second * 3): + fmt.Println("timeout 2") + } } + +// todo: cancellation? diff --git a/examples/timeouts/timeouts.sh b/examples/timeouts/timeouts.sh new file mode 100644 index 0000000..8a9dfb4 --- /dev/null +++ b/examples/timeouts/timeouts.sh @@ -0,0 +1,11 @@ +# Running this program shows the first operation timing +# out and the second succeeding. +$ go run timeouts.go +timeout 1 +result 2 + +# Using this `select` timeout pattern requires +# communicating results over channels. This is a good +# idea in general because other important Go features are +# based on channels and `select`. We'll look at two +# examples of this next: timers and tickers.