← Back to Go Course | Chapter 11: Concurrency | Lesson 4 of 8

The select Statement

select is like waiting at several doors at once, and stepping through whichever one opens first.
Syntax
go
select {
case v := <-ch1:
    // received from ch1
case ch2 <- value:
    // sent to ch2
default:
    // no channel ready
}

Waiting on Multiple Channels

select blocks until one of its listed channel operations can proceed, then runs that case's body -- it's how a goroutine can respond to whichever of several channels produces a value first.

Example: Waiting on Multiple Channels

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the fmt package
import "fmt"

// main is where the program starts running
func main() {
	// Short declaration: create ch1 and infer the type
	ch1 := make(chan string, 1)
	// Short declaration: create ch2 and infer the type
	ch2 := make(chan string, 1)
	ch1 <- "from channel one"

	select {
	case msg1 := <-ch1:
		fmt.Println(msg1)
	case msg2 := <-ch2:
		fmt.Println(msg2)
	}
}

Non-Blocking select with default

Adding a default case makes select non-blocking: if no other case is immediately ready, the default branch runs right away instead of waiting.

Example: Non-Blocking select with default

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the fmt package
import "fmt"

// main is where the program starts running
func main() {
	// Short declaration: create ch and infer the type
	ch := make(chan int)
	select {
	case v := <-ch:
		fmt.Println("received:", v)
	// default runs when no case matches
	default:
		fmt.Println("no value ready, moving on")
	}
}

Timeouts with select and time.After

Combining select with time.After lets a goroutine give up waiting on a channel after a deadline, which is the standard Go pattern for adding timeouts to blocking operations.

Example: Timeouts with select and time.After

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"fmt"
	"time"
)

// main is where the program starts running
func main() {
	// Short declaration: create ch and infer the type
	ch := make(chan string)
	select {
	case msg := <-ch:
		fmt.Println(msg)
	case <-time.After(50 * time.Millisecond):
		fmt.Println("timed out waiting for a value")
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Assuming select checks cases in order like a switch -- when multiple cases are ready, Go picks one at random, not top-to-bottom.
  2. Writing a select with no default and no case ever becoming ready, causing the goroutine to block forever.
  3. Forgetting a default case makes select non-blocking, immediately falling through if no channel is ready.
Chapter Summary
  • select lets a goroutine wait on multiple channel operations at once.
  • When multiple cases are ready simultaneously, select picks one at random -- there's no priority order.
  • A default case makes select non-blocking, running immediately if no channel is ready.
  • select is commonly used with time.After to implement timeouts on channel operations.
🔒

Chapter Quiz — Complete all 8 topics to unlock

0/8 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.