A Go select statement lets a goroutine wait on several channel operations at once. Each case sends to or receives from a channel; an eligible case runs, and the others are skipped. If several are eligible, Go chooses one at random. section 9.5.2
Consider a maze solver that launches workers for new paths but must stop when a worker finds the solution. Put select inside a for loop so the dispatcher can keep receiving paths while also listening for a quit signal. A quit case returns from the loop. section 9.5.2 section 9.7
Listing 9.26 explore.go: Stop listening to new messages
func (s *Solver) listenToBranches() {
wg := sync.WaitGroup{}
defer wg.Wait()
for {
select {
case <-s.quit: #1
log.Println("the treasure has been found, stopping worker")
return
case p := <-s.pathsToExplore: #2
wg.Add(1)
go func(p *path) {
defer wg.Done()
s.explore(p)
}(p)
}
}
}
The dispatcher handles either a quit signal or a path to explore. Its wait group keeps track of workers already launched. section 9.5.2
A default case can make a quick quit check before continuing exploration. But checking quit and then sending a branch as separate operations leaves a gap: quit may arrive between them. Put the branch send in the select instead, so the worker waits for either cancellation or a possible send. Neither case has priority when both are ready. section 9.5.2
select {
case <-s.quit:
log.Printf("I'm an unlucky branch, someone else found the treasure, I
give up at position %v.", pos)
return
case s.pathsToExplore <- branch:
//continue execution after the select block
}
This alternative selects between receiving the quit signal and sending the next branch. section 9.5.2
With no cases at all, select {} blocks forever. One use is keeping a Go WebAssembly program alive after it registers a function that JavaScript will call later. section 12.2.2








