A select
checks multiple channels for new value without blocking, the case with a non-blocking operation will be run. A select will often be used with a for
and used to process values.
select
WITHOUT adefault:
branch will wait for one channel to have a new value/message.select
with a default, if no channel has a new value, it will will run the default code.
for {
select {
case event := <-dbUpdateEvent:
if err := sendSSEEvent(w, event); err != nil {
return // handle error
}
// happy path done
case <-keepAliveTicker.C:
if err := sendSSEKeepAlive(w); err != nil {
return // handle error
}
// happy path done
case <-ctx.Done():
return // handle connection closed error
}
}
In this sample code we are processing server-sent events.
- The for is needed to keep sending more events after the first message is sent.
- It will either:
- send a
db
update to the client- if err, we return and we’re done looping
- or a keep alive when our ticker triggers.
- if err, we return and we’re done looping
- when the context is closed from a client disconnect, we’re done.
- send a
- However this code has a few issues. if we have an update from
dbUpdateEvent
at the same time of akeepAliveTicker
update, we’ll miss one of the two as the go runtime picks just one case. If we need to set priority (e.g. db update as that will also keep the connection alive) we need split it up into 2 selects, one perdbUpdateEvent
andkeepAliveTicker
(and then check if ctx is closed)