package my_select import ( "fmt" "net/http" "time" ) var defaultTimeout time.Duration = 10 * time.Second func Racer(a, b string) (winner string, err error) { return ConfigurableRacer(a, b, defaultTimeout) } func ConfigurableRacer(a string, b string, timeout time.Duration) (winner string, err error) { // aDuration := measureResponseTime(a) // bDuration := measureResponseTime(b) // if aDuration < bDuration { // return a // } // return b select { // Here we are simply waiting on the channel closing out // The first one that does so will return it's url // These are blocking operations because they are unbuffered channels. // This works though because `select` allows us to wait on multiple channels case <-ping(a): return a, nil case <-ping(b): return b, nil // Super handy function during select, returns a channel, waits the timeout, then //sends the current time that it was triggered at. // Helps get us out of a blocking case. case <-time.After(timeout): return "", fmt.Errorf("timed out waiting for %s and %s", a, b) } } // In our case, we don't care what type is sent to //the channel, we just want to signal we are done // and closing the channel works perfectly! // a chan struct{} is the smallest data type available // from a memory perspective func ping(url string) chan struct{} { // Notice how we have to use make when creating a channel; rather than say var ch chan struct{}. When you use var the variable will be initialised with the "zero" value of the type. So for string it is "", int it is 0, etc. // For channels the zero value is nil and if you try and send to it with <- it will block forever because you cannot send to nil channels ch := make(chan struct{}) go func() { http.Get(url) close(ch) }() return ch } // func measureResponseTime(url string) time.Duration { // start := time.Now() // http.Get(url) // return time.Since(start) // }