gergelyke / gergelyke.github.io

Personal website for nemethgergely.com
23 stars 2 forks source link

Learning Go as a Node.js Developer #14

Open gergelyke opened 6 years ago

tothandras commented 6 years ago

I think this line wasn't intentional: i, file := i, file. ๐Ÿ™‚

theckman commented 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.

judwhite commented 6 years ago

@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 ๐Ÿ˜„

ayuryshev commented 6 years ago

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.

  1. Dependency management

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.

  1. Asynchronicity

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 synchronicity now, golang already asynchronous in its nature.

theckman commented 6 years ago

@ayuryshev We did have our own leftpad moment just a few days ago... ๐Ÿ˜ž

donatj commented 6 years 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.

tothandras commented 6 years ago

@theckman, @judwhite Thank you for the explanation!

robbrit commented 6 years ago

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.

gergelyke commented 6 years ago

Thanks a lot @robbrit - makes sense. Still have to learn a lot about Go

chanlito commented 6 years ago

I still don't understand why go sync code is async? ๐Ÿคจ

robbrit commented 6 years ago

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.

truonglvx commented 6 years ago

waiting for new articles about: goroutines, middlewares, errors handling...