6 ms·
> Node.js on the other hand doesn't need any marketing/propaganda; it just sells itself. If Node.js used language X,Y,Z instead of Javascript, it would certain
by spriggan3 11y ago
> Node.js on the other hand doesn't need any marketing/propaganda; it just sells itself.
If Node.js used language X,Y,Z instead of Javascript, it would certainly not have been the success it was. "Javascript on the server" was the marketing/propaganda . Nodejs came at the right moment, Js was exploding, websockets were exploding, coffeescript and co were exploding in popularity.
What Go does better than Nodejs is a better standard library, true concurrency, static typing. With Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code. Writing callbacks is tedious, promises are tedious, co routines with yield need plumbing and async isn't in the spec.
> you're probably not qualified to manage this project at scale - Because scaling across multiple CPU cores is kids' play compared to scaling across multiple machines
With Node.js any heavy computation needs to be launched in a separate process, which is not the case with Go. sure you can have one/2/4/8 process dedicated to heavy computation, but it will still be the bottle neck. In the end you'll have to use a different language in order to solve the single threaded problem.
Finally there is no mystery as to why some businesses are moving from Node.js to Go: They didn't need anything Node.js provides at first place. They didn't need to use javascript, they didn't need to write "universal" applications with the same logic running in the client and in the server, they didn't need single threaded async programming.
Go is not a silver-bullet, but I was a bit sick of having to write a callback every time I wanted to make an http request, just to prototype something.
- Lukasa 11y agoI agree with almost all of your post except this: > With Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code. No, it can't, and pretending that it can is misleading in a way that allows large teams of developers to cause themselves real problems. The only sense in which this is true is that you can write a Go function with a simple signature like this: func DoSomeStuff (error) {} And the caller has no idea whether this involves concurrency under the hood (e.g. the function can spawn its own goroutines and channels as it sees fit). But, make no bones about it: this function blocks until it completes. This means that while this function may do concurrent work it is absolutely a blocking function. This is fine: sometimes blocking functions are good. But you cannot write a non-blocking Go function in the same way. To make a function non-blocking you can return a channel out of it for the return value to appear on, like this: func DoSomeStuff (chan error) {} In this model the caller really doesn't have to care whether the function is synchronous or not (though the fact that it was written this way strongly suggests that it is going to return asynchronously, or at least that the developer believes it will have to in the future). Except...that return value just there? That's a Future. It's a terrible, half-implemented version of a Future, but that's exactly what it is. It's a promise to return some kind of result at some point when the underlying process has returned. And if you don't want to block your current goroutine, you cannot block on that channel receive either. That means that you need a callback. There are two patterns for doing that: you could have some kind of central loop that selects over all channels like this and calls the callback functions (boy that looks a lot like Node's event loop, doesn't it!), or you can manually spawn your own callback functions in their own goroutines. Either way, you have callbacks and futures here: you're just building them yourself and calling them something different. There are lots of good reasons to switch to Go: it's a language that makes lots of developers remarkably productive, it has an ingrained philosophy of building concurrent programs, it's pretty damn fast, and it runs on all kinds of awesome platforms. But claiming that Go has learned something magic and new about how to write concurrent software in such a way that you don't have to care whether your code is async or not is just not true: you always have to care.
- agentS 11y ago> But you cannot write a non-blocking Go function in the same way. The caller can make function "non-blocking" by wrapping the call in a goroutine themselves. (There's some subtle differences, but they are mostly irrelevant here). For this reason, I'd say there is (almost) no reason to introduce asynchrony in your API in the way you suggest. The rest of your post built on this example seems shaky to me, since it seems built on an example API that doesn't need to exist. I'd say that "you don't have to care whether your code is async or not" is a overstating the case. I would append the qualifier "unless you're introducing concurrency". Considering that almost no low-level APIs are asynchronous, this usually happens rarely (or happens in low-level code like the HTTP server). Examples that have come up for me: making N parallel RPCs, writing a TCP server. In those situations, you care about async vs not. In event-loop based systems, it seems like async is in my face all the time, even when doing things that are entirely sequential.
- Lukasa 11y ago> The caller can make function "non-blocking" by wrapping the call in a goroutine themselves. Sure, but if they want the return value then either they need to construct the Future-y wrapper I just described or they need to assemble it together in a collection of other function calls wrapped inside a function that itself is either Future-y or uses a long-lived channel to communicate results. It is not novel to build up a non-blocking system from purely blocking method invocations. We've been doing that for years: it's called threading. Doing things this way has many advantages when written with appropriate diligence, and I'm not pretending otherwise. However, if you actually care about communicating between these arbitrary threads of execution than you either need Futures or queues (both of which are essentially just channels in Go), and at this point you've got the exact same problems as you get in NodeJS or any other asynchronous programming environment. > The rest of your post built on this example seems shaky to me, since it seems built on an example API that doesn't need to exist. I don't think that's fair: as I mentioned above, the fact that you as library author would not write the Future-y extension doesn't mean that the Future-y extension isn't built: you just force your caller to build it. That's fine, it's a perfectly good architectural decision (probably you should't be making those decisions for your user), but it doesn't remove the problem. > I'd say that "you don't have to care whether your code is async or not" is a overstating the case. I would append the qualifier "unless you're introducing concurrency". Sure. The thing that matters here is that Node is always introducing concurrency, because Node is concurrent. This is why all Node programs have to care about concurrency: they are all concurrent because their system is concurrent. This is desperately inconvenient for many one-off programs, which is why I personally don't use Node for anything like that: I'd much rather use Python or Rust or Go. But that was never my argument. My argument was about OP's assertion that "with Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code. Writing callbacks is tedious, promises are tedious, co routines with yield need plumbing and async isn't in the spec." The first sentence is dangerously misleading (while technically true, any system that does that is usable only in that one context), and the second one misses the point, which is that those things get effectively built anyway in any moderate-scale concurrent system in Go. But my biggest point is this: Go isn't magic in regard to concurrency, and there is a weird amount of magical thinking around Go. Go is a very good language with a lot to like, and I like it quite a lot. But when boiled down to it, Go's concurrency model is threads with a couple of really useful primitives. And that's great, and it works really well. But it's not new or novel. The sentence "with Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code" is equally true if you replace "Go" with "C", or "Python", or "Java", or any language with a threaded concurrency model. There's no magic here. It's the same building blocks everyone else is using.
- erlich 11y ago> What Go does better than Nodejs is a better standard library What is so hard about `require('lodash')`, or `require('moment')`? > true concurrency The trade-off is you get great concurrency without thinking about it. If you are working in Go, you have to start thinking about it as you go. You can always think about it as you go with Node.js if you want, but you don't have to and you will still get quite far. > static typing See http://flowtype.org http://flowtype.org or http://www.typescriptlang.org http://www.typescriptlang.org. With more advanced type systems than Go. > With Go it doesn't matter if an operation is blocking or non blocking, that fact can totally be abstracted from the client code. Writing callbacks is tedious, promises are tedious, co routines with yield need plumbing and async isn't in the spec. You can use ES7 async/await and promises today. That it "isn't in the spec" probably won't be for too much longer. async function() { const {data} = axios.get('foo') const results = [] const arr = ['foo', 'bar', 'baz'] for (const i in arr) { results[i] = await axis.get(arr[i]) } return results }
- gotchange 11y agoasync function() { const {data} = axis.get('foo'), arr = ['foo', 'bar', 'baz']; return arr.map(ele => await axis.get(ele)); } I rewrote your async function making it more concise and compact. Isn't advisable not to use «for in» with arrays?
- Arnavion 11y agoreturn arr.map(ele => await axis.get(ele)); is not legal - the await has no corresponding async. You may have wanted to write return arr.map(async ele => await axis.get(ele)); but that's the same as return arr.map(ele => axis.get(ele)); (Assuming axis.get returns a promise.) That is, it returns an array of promises of values, not a promise of an array of values like erlich's function. erlich's example is correct, with the replacement of for-in with for-of. Edit: Also, a solution using Array.map() is not impossible: return Promise.all(arr.map(ele => axis.get(ele))); but this has different characteristics than the original function. The original ran the tasks in series whereas this runs them in parallel.
- 11y ago