One thing the author didn't mention is that Go will use multiple cores in this example, whereas Node is single-threaded. Right?
One thing the author didn't mention is that Go will use multiple cores in this example, whereas Node is single-threaded. Right?
Basically if you say this:
fn()
The Go runtime will execute the function in its entirety and wait for a return value.If you say this:
go fn()
The Go runtime will execute that function in a new goroutine (the return value is discarded) and then immediately proceed to the next line in the current goroutine.So if you have some callDB() function that performs some long-running i/o, in node, you might do something like this to perform that operation without blocking the surrounding code:
...
callDB(someparam, otherparam, function (result) {
// do something with result here
})
...
while in Go, you would do something more like this: ...
go func() {
result := callDB(someparam, otherparam)
// do something with result here
}()
...
which winds up being more like this func superGreat() {
result := callDB(someparam, otherparam)
// do something with result here
}
go superGreat()
With the benefit being that if you want superGreat to run concurrently, you use the go keyword, and if you want it to be run in a synchronous style, you just call it normally.The problem with Ruby/Python/etc is that it is awkward to take something that is blocking and make it nonblocking. The problem with node.js is that node.js says "blocking is bad; therefore, never block". Go takes a different approach, in that it makes it obvious to know how to write something so that it does or does not block, so that the developer is free to use whatever I/O paradigm fits the problem at hand.
The net/http package allows you to register a handler, which is a function that takes and http request and writes a response. There is a main request loop that, when receiving a request, will create a new goroutine for each request and execute its handler in its own context. So within your handlers, you mostly just write blocking code, because you're already in your own isolated goroutine, and the only thing you'd be blocking is the processing of the current request. If you want to do something in the background, you run it in a new goroutine. goroutines are very cheap, so you can be quite cavalier about their usage.
If you want to use a callback-passing style, you can do that in Go (because it has function literals and closures and first-class functions and all that), but that's not idiomatic by a longshot.
The absolutist position that node.js takes by saying that "all i/o must be nonblocking" is no better than our previous options of "all i/o is blocking". Sometimes the simplest and most readable solution is simply to block. There is a yin to the yang of blocking.
In any case:
Node lets you reuse the same code on both the client and server. This can be very valuable if you're writing a JS-heavy webapp. In my experience, the most useful bits of code are the in-code data representation (models in an MVC world). Being able to have identical functionality client- and server-side can save a lot of time.
Node also has Socket.IO, which is very useful if you want to use websockets. (Go has a library for it, but it hasn't been updated in a long time.) The vast majority of the Node ecosystem revolves mostly around building interaction- and communication-heavy webapps. I don't know Go or its community at all, but I don't believe they have the same kind of single-minded focus that Node has. If you're trying to do the things Node does well, you will likely benefit from the community support.
There are, of course, many downsides to Node as well, but much as I dislike Javascript, I still think it's one of the best choices for writing highly interactive webapps right now.
I wouldn't use node if it wasn't for this because I don't particularly like javascript.
All my other games have used Python on the back end and you can really feel how clunky and painful javascript is when you have to switch between the two five times a day.
I'd love to see a second set of benchmarks of the same code on multiple cores.
Well, for starters, Go is much more awkward for working with JSON than JS is (which is to be expected since JSON is literally a subset of JavaScript). More importantly, Go lacks the ridiculously vibrant ecosystem for Web development that Node has — no NPM, no Connect, no Jade, no Stylus, etc.
What I've done sometimes is just clone the repo within a directory that appears in the GOPATH, maybe checkout a specific revision, and that solves the problem.
type UploadProgress struct {
Progress int `json:"progress"`
}
//...
// variable progress is of type Progress
if json_data, err := json.Marshal(progress); err == nil {
w.Header().Set("Content-Type", "application/json")
w.Write(json_data)
}
json.Unmarshal works the same way. IMHO, not exactly more awkward than using JSON.parse and JSON.stringify.In retrospect, I think I may have overstated it a little bit, and I doubt this problem would crop up too often for most apps.
It's for an older version of Go, and has only been tested on Plan 9, but it shows a static content + blog server, with JSON config files and templated HTML for the blog pages.