Am learning Google Go (very interesting, by the way) and the first program I used to learn was Matt Aimonetti's blog post:
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.
fmt.Sprintf
, it returns a string, doesn't actually print it, you want to use fmt.Printf
fmt.Println(ms/1000)
will print the seconds.