The non-blocking read parts is also easy, but that's also only one use-case for Go's select. The harder part is the generic "wait until one of the select cases can be taken".
You can't implement this with non-blocking reads alone. If you would, it would be something like busy spinning and iterating through all select cases and trying none-blocking reads until one succeeds. But that's just not efficient.
You need a mechanism instead which also registers a pending select at each event source and unregisters it when done. And the event source (e.g. channel) must wake up one or more select when it's state changes.