Open gergelyke opened 6 years ago
@tothandras I think it was intentional. When using the range
operator, Go doesn't initialize new variables on each loop. This means it's setting the value each loop (=
), instead of creating a new variable (:=
). If you aren't using goroutines
that's not an issue. However, this is important when it comes in to using goroutines with inline functions like this.
If you're using goroutines
where you aren't explicitly passing the variables in, you can actually end up in a situation where the goroutines all start with the last value returned from the range
operator. This is because they are the same variable.
One way to get around this is to make a locally scoped variable, or variables in this case. The other way is to pass the variables in to the function you're invoking.
You also see this pattern used in Go unit tests, where parallel tests are executed. They do it for the same reason.
@tothandras This shadows the iterator variables to prevent access to modified closures inside the goroutines. I would have used different variables names to make it more clear. You can use go vet
to fail CI when there's shadowing as it's often more confusing than it's worth. Here's an example which I think makes it more clear: https://play.golang.org/p/sN4GJIJzMuS
@theckman The heck, man? You got me by a few minutes ๐
It's only natural that you're trying to find solutions for problems you encountered in past.
But exploring new technology this way leads to absolutely natural mistakes. You're are worring about wrong things.
This problem is not (and never was) in such terrible scale as in javascript.
You can forget about this problem while you learn go.
I suppose that dep
will be released offically when you will really need this.
I did the same mistake as you: I thought that channels and messages are similar to Actor pattern in Erlang, Akka.
Don't try to think about promises
, futures
and other abstraction when you programming in go.
In fact, you need learn sync
hronicity now, golang already asynchronous in its nature.
@ayuryshev We did have our own leftpad
moment just a few days ago... ๐
I'd be surprised if someone else hasn't already said this, but I'd say it's better to come to terms with the built in HTTP server before looking at a Framework like Gin.
It's added dependency / overhead and in practice usually not necessary. The built in server is quite mature and production ready.
We personally have production servers fielding several thousand requests per second using the built-in HTTP server.
It's honestly very flexible when you get your head around the interfaces it uses, and for things like more complex routing, CORS, etc โ http://www.gorillatoolkit.org/ Gorilla components can be used to supplement rather than replace.
@theckman, @judwhite Thank you for the explanation!
Re: The async Go code
No Go developer would write code like this. Asynchronous calls are useful when you have a limited number of threads in a pool and you want to re-use them while waiting for I/O to come back. Go's internal goroutine scheduler handles this already, so there is no need to complicate your life by writing async code.
Thanks a lot @robbrit - makes sense. Still have to learn a lot about Go
I still don't understand why go sync code is async? ๐คจ
It's not async actually, it's sync. Async calls are only needed when you have a limited number of threads. In Node.js you only have one thread, so you are very limited, but even in Java/C#/etc. which typically use thread pools of a small size you are still limited. If you have high throughput (like with a web server) you can end up running out of threads; async solves that problem by returning threads to the pool when they are waiting for I/O.
In Go (and any other system that uses fibers) you are not as limited. You can just fire off goroutines without really worrying. If a goroutine is blocked because it is waiting on I/O, it doesn't matter since you're not wasting a scarce resource and so dealing with the complexity that async introduces is not worth the effort.
Example (Go-ish pseudocode):
x = http.Get(url)
y = someServiceCall(x)
z = someOtherServiceCall(y)
// stuff
vs.
http.Get(url, func(x) {
someServiceCall(x, func(y) {
someOtherServiceCall(y, func(z) {
// stuff
})
})
})
Technically you could use promises/futures here, but they are still conceptually weird compared to sync code, especially when you have to start dealing with failure scenarios.
waiting for new articles about: goroutines, middlewares, errors handling...
I think this line wasn't intentional:
i, file := i, file
. ๐