select lets a goroutine wait on multiple channel operations at once, similar to switch but for channels. It blocks until one case can run, then runs that one.
A basic select with two channels. Whichever channel has a value ready first wins; if both are ready at the same instant, Go picks one at random.
select {
case msg1 := <-ch1:
fmt.Println("received", msg1)
case msg2 := <-ch2:
fmt.Println("received", msg2)
}Add a default case to make select non-blocking. If no channel is ready, default runs immediately instead of waiting.
select {
case msg := <-ch:
fmt.Println("received", msg)
default:
fmt.Println("no message ready")
}time.After returns a channel that fires once, after the given duration. Pairing it with select adds a timeout to any channel operation.
select {
case res := <-results:
fmt.Println("received:", res)
case <-time.After(200 * time.Millisecond):
fmt.Println("timed out")
}Putting it together: a goroutine does some work and sends the result on a channel, while select races that against a 200ms timeout.
package main
import (
"fmt"
"time"
)
func main() {
results := make(chan string)
go func() {
time.Sleep(50 * time.Millisecond)
results <- "done"
}()
select {
case res := <-results:
fmt.Println("received:", res)
case <-time.After(200 * time.Millisecond):
fmt.Println("timed out")
}
}The goroutine finishes in 50ms, well under the 200ms timeout, so the results case wins.
$ go run select.go
received: done