5 comments

[ 135 ms ] story [ 909 ms ] thread
It's a bit odd to do the time.Sleep in this code because it means that select is often not able to receive on the ch channel:

  select {
      case r := <-ch:
          fmt.Printf("%s was fetched\n", r.url)
          responses = append(responses, r)
          if len(responses) == len(urls) {
              return responses
          }
      default:
          fmt.Printf(".")
          time.Sleep(5e7)
      }
It would be better to use time.Tick

  ticker := time.Tick(50 * time.Millisecond)

  select {
      case r := <-ch:
          fmt.Printf("%s was fetched\n", r.url)
          responses = append(responses, r)
          if len(responses) == len(urls) {
              return responses
          }
      case <-ticker:
          fmt.Printf(".")
      }
Also, there's no need to use a buffered channel. Each goroutine that's handling a URL get can just wait for the send on the channel before terminating.

  ch := make(chan *HttpResponse, len(urls)) // buffered
can just be

  ch := make(chan *HttpResponse)
And another thing that could be done is to not have asyncHttpGets return a slice of *HttpResponse but have it return a channel and then range over it. Then asyncHttpGets could run in its own goroutine and just close the channel when done.
Thanks, I'll make the appropriate changes.
This is indeed what Go is great for. But your code example is a bit wonky :) Specifically,

    for {
        select {
        case r := <-ch:
            // do something with r
        default:
            fmt.Printf(".")
            time.Sleep(5e7)
        }
    }
You're needlessly blocking 50ms on every iteration. (Side note: time.Sleep(50 * time.Millisecond) better captures your intent.) Better to reformulate that as

    for {
        select {
        case r := <-ch:
            // do something with r
        case <-time.After(50*time.Millisecond):
            fmt.Printf(".")
        }
    }
Or, if you don't need the dots in the output,

    for i := 0; i < cap(ch); i++ {
        r := <-ch
        // do something with r
    }
Post updated with some of your suggestions as well as some from @jgrahamc Thanks!

    Go implements OOP slightly differently than other
    languages. Functions are defined on an interface, 
    not a class or subclass.
This is not quite correct. Functions are defined on concrete types. Interfaces are named collections of functions, used to describe behavior.