I’m new to Go and very inexperienced with it so forgive me if this is a simple matter. The code below should output to the browser the strings in whatever order they were returned from the goroutine one at a time rather than waiting for all to return and displayed at once. I originally tried this with a single channel to return a response and than display it which didn’t work, now I tried to do it with a second channel to output. Here is the code:
package main
import (
"net/http"
"fmt"
"time"
)
var l = []string{
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
c := make(chan int)
s := make(chan string)
for i, _ := range l {
go doSomething(i, c)
}
for i :=0; i < len(l); i++ {
go func(j int, c chan string) {
time.Sleep(1000*time.Millisecond)
//fmt.Fprintf(w, l[j] + "\n")
s <- l[j]+"\n"
}(<-c, s)
}
for i :=0; i < len(l); i++ {
f, _ := w.(http.Flusher)
fmt.Fprint(w, <-s)
f.Flush()
}
})
fmt.Println("Listening on localhost:8080")
http.ListenAndServe(":8000", nil)
}
func doSomething(i int, c chan int) {
c <- i
}
If you can point me in the right direction I would greatly appreciate it.
Got it working finally, here is the working code (the comments are for my understanding…):
package main
import (
"net/http"
"fmt"
"time"
"math/rand"
)
var list = []string{
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// this header had no effect on results, kept it anyway
w.Header().Set("Connection", "Keep-Alive")
// these 2 headers were needed in order to get the http chunked incrementally
w.Header().Set("Transfer-Encoding", "chunked")
w.Header().Set("X-Content-Type-Options", "nosniff")
// creating a channel for bi-directional communication
c := make(chan int)
// loop through and spawn off threads
for i, _ := range list {
go doSomething(i, c)
}
// used to flush html incrementally, without it the html won't
// display until all threads have been executed
f, _ := w.(http.Flusher)
// join our threads back to the main func thread
for i :=0; i < len(list); i++ {
// create a time.Duration to multiply against milliseconds
t := time.Duration(random(100, 499))
// sleep to simulate waiting for a response
// possibly from a remote service call etc...
time.Sleep(t * time.Millisecond)
// print value to browser
fmt.Fprint(w, list[<-c] + "\n")
// flush it to the browser as it's ready without waiting for
// everything to finish executing
f.Flush()
}
})
// spawn off a server on port :8000
fmt.Println("Listening on localhost:8000")
http.ListenAndServe(":8000", nil)
}
// a function to convert into a go routine
func doSomething(i int, c chan int) {
c <- i
}
// generate a random number given min/max values and returns an int
func random(min, max int) int {
// create a seed to guarantee a unique value each time
seed := rand.NewSource(time.Now().UnixNano())
// pass our seed to our new rand generator
r := rand.New(seed)
// return a random value between min and max
return r.Intn(max - min) + min
}
You’re absolutely right, I eliminated that at one point as I was testing different solutions. I’m only 2 weeks or less into Go and it’s not as easy for me to learn as other languages in the past. Thanks for the suggestion!