Lesson 13 · 13 of 20
Buffered channels and select · Go
Use buffered channels as queues in Go, send without blocking using select and default, and set a time limit with select and time.After.

make(chan string, 3) creates a buffered channel: it holds up to three messages before anyone receives them. It works like a queue of pending emails. len(queue) tells you how many are inside and cap(queue) how many fit.
`select` waits on several channel operations at once and runs the first one that can go ahead. With a case queue <- msg and a `default`, the send no longer blocks: if there is room, the message is queued; if the buffer is full, default runs and the message is dropped. That is why newsletter and survey do not get in.
After close(queue) you can no longer send, but the messages already in the buffer are still there: range delivers all of them, in order, and stops once the queue is empty.
The second part sets a time limit. A goroutine takes 300 ms to prepare a report; time.After(50 * time.Millisecond) returns a channel that receives a value after 50 ms. The select takes whichever comes first: the deadline. The report channel has a buffer of 1 so the goroutine can leave its result even when nobody is waiting any more, instead of being stuck forever.
If several cases are ready at once, select picks one at random. This example avoids that on purpose: either only one case is possible, or the times are far apart (50 against 300 ms), which is why the output is always the same.
Code · main.go
package mainimport ( "fmt" "time")func main() { queue := make(chan string, 3) // buffered: holds 3 messages without a receiver for _, msg := range []string{"welcome email", "invoice 17", "password reset", "newsletter", "survey"} { select { case queue <- msg: fmt.Printf("queued %-15s len=%d cap=%d\n", msg, len(queue), cap(queue)) default: // the buffer is full: do not block, drop the message fmt.Println("full, dropped", msg) } } close(queue) for msg := range queue { // the buffered values are still delivered after close fmt.Println("sending", msg) } report := make(chan string, 1) // buffer 1: the sender never gets stuck go func() { time.Sleep(300 * time.Millisecond) // a slow job report <- "sales report ready" }() select { // waits for whichever case is ready first case r := <-report: fmt.Println(r) case <-time.After(50 * time.Millisecond): fmt.Println("timeout: report not ready after 50ms") }}Output · go run main.go
queued welcome email len=1 cap=3 queued invoice 17 len=2 cap=3 queued password reset len=3 cap=3 full, dropped newsletter full, dropped survey sending welcome email sending invoice 17 sending password reset timeout: report not ready after 50ms
Key points
make(chan T, n)creates a channel with a buffer ofnvalues.selectruns the first channel operation that is ready.- With
default,selectnever blocks. - After
close,rangestill delivers what is left in the buffer. time.Afterinside aselectsets a time limit.
Exercise
Change the capacity of queue to 5 and check that nothing is dropped now. Then change the deadline to 500 * time.Millisecond and watch the report win instead.