增加的秒数不打印

Am learning Google Go (very interesting, by the way) and the first program I used to learn was Matt Aimonetti's blog post:

Real life concurrency in Go

package main 

import (
    "fmt"
    "net/http"
    "time"
)

var urls = [] string  {
    "http://golang.org",
    "http://www.espn.com",
    "http://www.google.com",
}

type HttpResponse struct {
    url       string
    response  *http.Response
    err       error
}

func asyncHttpGets(urls [] string) [] *HttpResponse {
    ch := make(chan *HttpResponse)
    responses := [] *HttpResponse{}
    seconds := 0
    for _, url := range urls {
        go func(url string) {
            fmt.Printf("Fetching %s 
", url)
            resp, err := http.Get(url)
            ch <- &HttpResponse{url, resp, err}
        }(url)
    }

    for {
        select {
        case r := <-ch:
            fmt.Printf("%s was fetched
", r.url)
            responses = append(responses, r)
            if len(responses) == len(urls) {
                return responses
            }
        case <- time.After(50 * time.Millisecond):
            seconds++
            fmt.Printf(".")
            fmt.Sprintf("%v", seconds)
        }
    }
    return responses
}

func main() {
    results := asyncHttpGets(urls)
    for _, result := range results {
        fmt.Printf("%s status: %s
", result.url, result.response.Status)
    }
}

I wanted to see the milliseconds also printed in the output so I create a seconds variable inside the asyncHttpGets method and incremented it in the same place that prints the "."

However, it never displays when I run:

 go build concurrency_example.go && ./concurrency_example

Outputs:

Fetching http://golang.org 
Fetching http://www.espn.com 
Fetching http://www.google.com 
...http://www.google.com was fetched
..http://golang.org was fetched
.........http://www.espn.com was fetched
http://www.google.com status: 200 OK
http://golang.org status: 200 OK
http://www.espn.com status: 200 OK

Question(s):

Why isn't the seconds counter being printed?

How do I print it by converting milliseconds to seconds (as int) and then convert to string?

This example and the explanations in the blog post was a great learning experience!

As others mentioned the issue is that you used fmt.Sprintf which formats a string according to a format (the first argument of the function call).

Replace fmt.Sprintf("%v", seconds) by fmt.Printf("%v ", seconds) to print the incremental value or fmt.Printf("%v ", seconds/1000) if you want to print milliseconds. That said a better/more accurate way to get the elapsed time between the start of the function and when the time prints the time would be to use the time package as shown below:

package main

import (
  "fmt"
  "net/http"
  "time"
)

var urls = []string{
  "http://golang.org",
  "http://www.espn.com",
  "http://www.google.com",
}

type HttpResponse struct {
  url      string
  response *http.Response
  err      error
}

func asyncHttpGets(urls []string) []*HttpResponse {
  ch := make(chan *HttpResponse)
  responses := []*HttpResponse{}
  for _, url := range urls {
    go func(url string) {
      fmt.Printf("Fetching %s 
", url)
      resp, err := http.Get(url)
      ch <- &HttpResponse{url, resp, err}
    }(url)
  }
  start := time.Now()

  for {
    select {
    case r := <-ch:
      fmt.Printf("%s was fetched
", r.url)
      responses = append(responses, r)
      if len(responses) == len(urls) {
        return responses
      }
    case t := <-time.After(50 * time.Millisecond):
      fmt.Println(t.Sub(start))
    }
  }
  return responses
}

func main() {
  results := asyncHttpGets(urls)
  for _, result := range results {
    fmt.Printf("%s status: %s
", result.url, result.response.Status)
  }
}

We are collecting the timer output returned by time.After and subtracting the start time we set when the function is called. We end up with a time.Duration value which we can print many different ways.

Q: Why isn't the seconds counter being printed?

A: Because you used fmt.Sprintf, which returns a formatted string; it doesn't print that string. Use fmt.Printf.

Q: How do I print it by converting milliseconds to seconds (as int) and then convert to string?

A: If you have a value in milliseconds, simply divide by 1000 to get seconds. fmt.Printf will convert it to a string for you.

  1. You're using fmt.Sprintf, it returns a string, doesn't actually print it, you want to use fmt.Printf
  2. Simply fmt.Println(ms/1000) will print the seconds.