7. Select

Sometimes we have a single goroutine listening to multiple channels and want to process data in the order it comes through each channel.

A select statement is used to listen to multiple channels at the same time. It is similar to a switch statement but for channels.

select {
case i, ok := <-chInts:
	if ok {
		fmt.Println(i)
	}
case s, ok := <-chStrings:
	if ok {
		fmt.Println(s)
	}
}

The first channel with a value ready to be received will fire and its body will execute. If multiple channels are ready at the same time then one is chosen randomly. In the example above, the ok variable is a boolean. It is true while the channel is open, and false when the channel is closed by the sender.

Assignment

Complete the logMessages function.

Use an infinite for loop and a select statement to log the emails and sms messages as they come in order across the two channels. Add a condition to return from the function when one of the two channels closes, whichever is first.

Use the logSms and logEmail functions to log the messages.

Solution

What it does, in plain terms:

  • select waits until either chSms or chEmails has a value ready.

  • When one is ready, it receives it and logs it immediately (logSms / logEmail).

  • If a receive returns ok == false, that channel is closed → you return and stop (per “whichever closes first”).

circle-info

Closed-channel behavior: once a channel is closed, receiving from it is always immediately ready (it returns the zero value + ok=false). That means if you didn't return, your select would keep picking the closed channel over and over. Returning avoids that problem.

circle-info

Non-deterministic order: if both channels happen to have a value ready at the same time, Go chooses one case “randomly”. So you'll get “arrival order” in the practical sense, but ties aren't stable.