8 ms·
It's worse than that: it's true that in-process asynchronicity is a performance optimization, but it doesn't follow at all that the code needs to read asynchron
by ef4 13y ago
It's worse than that: it's true that in-process asynchronicity is a performance optimization, but it doesn't follow at all that the code needs to read asynchronously.
There is no performance reason to make developers hand-code in the continuation-passing style. That should be the job of a compiler or runtime environment.
Erlang does it. Stackless Python does it. Go does it.
The whole "look how fast Node is due to all my hand-rolled callbacks" is a total fallacy. It's a case of taking perverse pride in an accidental wart of your language.
Slowly the node community is maturing and beginning to realize that if they want to build robust, fault-tolerant software they need better primitives than hand-rolled callbacks. Hence the slowly rising mindshare of Promises, which are strictly better, but would be better still if the language itself baked them in implicitly (consider that nearly any value in a Haskell program is represented by a promise under the hood, but you never have to deal with them explicitly).
- jeswin 13y agoThere is no performance reason to make developers hand-code in the continuation-passing style. node certainly recognizes this. However, there is no need to do anything here since JS is taking care of this with ES6. Even if you don't want to use ES6, there are now multiple ways to compile it down to ES5. Hence the slowly rising mindshare of Promises Promises are declining, and honestly doesn't really solve the problem in most cases. A couple of years from now, you won't see promises on the server. You should see tjholowaychuk/visionmedia's co[1]; that's how JS is going to look like in future. In terms of expressiveness, there isn't much to choose between dynamic languages. While it took some getting used to, programming JS wasn't that different from writing Python. However, JS/node is succeeding because this is the first time ever that we have a language/platform that is truly write-once, run-anywhere. A significant advantage in favor of node. [1] https://github.com/visionmedia/co https://github.com/visionmedia/co
- ef4 13y agoYes, I'm looking forward to ES6. And I agree that Node's strength is that Javascript is truly the lingua franca of the modern web, which I why I would even bother using it. But the value of running everywhere is also why ES6 is not a panacea. As you say, soon we won't need promises "on the server". But I want my codebase to run correctly everywhere. Promises are a bandaid that I'll need for quite a while yet.
- phillmv 13y agoIf you're not shipping your js code to the browser, then it's frankly kinda silly to write it on the server.
- ulisesrmzroche 13y agoNo one pay attention to this dude. Of course it isn't silly. Plenty of people build their servers with JS and put them into production.
- twic 13y agoPlenty of people build their servers with PHP and put them into production. It is still germane to ask if this is silly. for LANG in Java Scala Ruby Python C C++ Groovy Clojure; do s/PHP/$LANG/; done
- ulisesrmzroche 13y agoSomething silly is like putting shit-tons of fish into a box and expecting it to make HTTP requests. Writing a web server in PHP, regardless of what you think of the language, isn't. Comments like that are just gonna clutter someone's Google search one day.
- phillmv 13y ago"Right tool for the right job". If all you have is a room full of PHP developers and all you need is a REST site, then well you could do worse but I would like to discourage you from the practice. Similarly, unless you have a highly specialized need - fun asynch chat server or complex app where business logic is being directly shared between client and server - I too would discourage locking one's self into that platform. Javascript is not the worst thing ever, but why bother if you don't have to?
- ulisesrmzroche 13y ago
- babby 13y ago>A couple of years from now, you won't see promises on the server. You should see tjholowaychuk/visionmedia's co[1] Dont you mean you won't see standalone Promises, and instead Promises + Generators?
- masklinn 13y ago> Promises are declining, and honestly doesn't really solve the problem in most cases. A couple of years from now, you won't see promises on the server. You should see tjholowaychuk/visionmedia's co[1]; that's how JS is going to look like in future. I'm impressed that you apparently completely failed to realise co is an abstractive layer over promises, something it states upfront: > Generator based flow-control goodness for nodejs (and soon the browser), using thunks or promises What you yield from your generator is not magical pixie dusts, it's promises.
- sync 13y agoActually, it's not promises. It's generators. co thunkifies promises: https://github.com/visionmedia/co/blob/master/index.js#L209-L215 https://github.com/visionmedia/co/blob/master/index.js#L209-...
- masklinn 13y ago> Actually, it's not promises. It's generators. The generator is the driver, but the driver on its own its useless if it's got nothing to drive. And what it has to drive is promises (or thunks since most existing node code is thunks-based I guess it makes sense to support that. taskjs uses promises more or less exclusively for the same purpose, Python 3.4's asyncio uses behaving much like JS promises, etc...)
- jeswin 13y agoSeems you have misunderstood how it works. https://github.com/visionmedia/co/blob/master/index.js https://github.com/visionmedia/co/blob/master/index.js Typically in apps using 'co', functions will return generators, not promises. Promises were probably added because of all the existing code out there. Here is a file from my ongoing project, should explain how generators are being used. See getPosts() or addPost() https://github.com/jeswin/fora/blob/master/server/src/models/forum.coffee https://github.com/jeswin/fora/blob/master/server/src/models... [Edit: Just read your earlier (Gr.GP) comment. Might want to avoid the unnecessary snark, especially when there's a chance you might be wrong.]
- angersock 13y agoThe linked page doesn't really convince me. What do we get using this new shiny instead of the older shiny bluebird or the old shiny Q?
- ephemeralgomi 13y agoPromises are great for replacing callbacks in the sense of "Here is a function I want you to call once and only once". You can't convert the callback parameter of http.Server.listen() to a promise. I don't really get the confusion. First-class functions let you do all kinds of great things - I can do things like map across a list to generate a list of functions which I then pass to somewhere else that can left-fold them, which I have trouble visualizing how I would do in a pseudo-synchronous language. I understand that people have strong preferences for how they like to write code and that's why it's great that there are tons of programming languages out there. Javascript is one of them.
- EGreg 13y agoFor this, Node.js is supposed to run several processes. While one is tied up, another one works. This is almost as good as erlang actor model. You can have a small scheduler library figuring out who to send the next request to. What I do think is that Node.js processes should be expected to crash at any time. Because a Node.js process serves many requests, it can run out of memory, or anything else. Node.js processes should be able to be restarted instantly, with the consequence of only a few dropped requests (in fact, the requests should be retried if they fail due to a crash, before reporting a failure to the client).
- bascule 13y agoFirst: reactor-based event loops aren't always a performance optimization. Round tripping I/O operations through an event loop adds latency. Node's approach is great if you have large numbers of mostly idle connections, such as in a chat or websocket server, but slower than a multithreaded, blocking approach when you have small numbers of active connections. What you really want is a system that supports both models, so you can choose the best one for your problem. Some Node developers, for example the ones behind Meteor, have come to the conclusion that callback/promise-driven development is painful and tedious, and started wrapping up the asynchronous behavior in coroutines. Unfortunately, to participate in this sort of world, where you wrap the asynchronous spaghetti into what the "Node.js Is Badass Rockstar Tech" video would call "sequential code, you know, the code you can read", each async library must be hand-plummed into a synchronous version that does the coroutine juggling. This puts Node in exactly the situation it was trying to escape: now there are two different I/O models, one synchronous, one asynchronous, and not all libraries support the synchronous model, so by trying to leverage it, you're cutting yourself out of large parts of the Node ecosystem. Better environments abstract over the I/O scheduling, letting you choose between threaded, blocking I/O, and a M:N task scheduler which can handle many lightweight tasks being scheduled on native threads. Rust comes to mind.
- ulisesrmzroche 13y agoIt's all async under the hood because, in the end, you can only really resolve one thing at a time. Just because generators and promises let you write a more synchronous style of programming, essentially sugar, it doesn't mean that it's actually working in a true sync fashion.
- rdtsc 13y agoI wrote a post on this in a Go topic here not too long ago: https://news.ycombinator.com/item?id=7388790 https://news.ycombinator.com/item?id=7388790 It was about how essentially, in a large concurrent system, a chain of callbacks like: cb1 -> cb2 -> cb3|eb3 then cb3->cb4 and eb3->cb5 (select/epoll returns and calls cb1, chain ends with cb4 or cb5 depending if errback eb3 is called). Is just a messy, dangerous and confusing re-implementation of threads/goroutines/tasks. Besides spreading the business logic among multiple io related function or sprinkling yields() or thens() it doesn't completely save one from needing locks and semaphores if shared or non-local data is modified. A second callback chain from cb1 could have started before the previous one finished. Now they both could be modifying the same data. Yes granularity level in this is at code block level between IO points not assembly instruction, as the system grows large this problems becomes apparent. I had to deal with it in a Python Twisted based framework. There is Twisted Semaphore and I had to use it. The node.js and reactor-based event loops look _very_ nice in small demos and when the callback chain is shallow. HAproxy or nginx are good examples of this. They have shallow callback chain. Node.js demo example also look good, "Oh look you can serve 'Hello World' in 5 lines on a websocket!'" stuff like that. As systems grow larger, callbacks chains as concurrency mechanisms start to suck.
- nawitus 13y agoAccording to Wikipedia, Go's concurrency-model is not safe, and that's a huge problem in my opinion: "There are no restrictions on how goroutines access shared data, making race conditions possible. Specifically, unless a program explicitly synchronizes via channels or mutexes, writes from one goroutine might be partly, entirely, or not at all visible to another, often with no guarantees about ordering of writes.[36] Furthermore, Go's internal data structures like interface values, slice headers, and string headers are not immune to race conditions, so type and memory safety can be violated in multithreaded programs that modify shared instances of those types without synchronization."
- irahul 13y agoIdiomatic go concurrency uses channels for synchronization and passing data around. You shouldn't be writing to shared data from multiple co-routines.
- bascule 13y agoIt's easy to say that, but when Go added a race detector, they immediately found 40 data races in the standard library
- rdtsc 13y ago
- cmbaus 13y agoUsing synchronous I/O is only simpler if you never have to synchronize threads, which you always do. After taking a closer look at Discourse, it seemed to me that there was something amiss about how the Ruby community handles concurrency -- non-blocking servers (thin), with blocking database I/O (ActiveRecord), behind a round robin load balancer (nginx), combined with external processes for asynchronous tasks (sidekiq). That is a lot of tooling to handle concurrency. Comparatively I think Node has some advantages. I am a proponent of Node, not because I think it is perfect, but I do think it does many things right. If you are building a significant web app you can't avoid JavaScript. And it is nice to be able to use and move code between the client and the server. Also, this might sound weird, but I think there are some benefits to forcing developers to think differently about blocking vs non-blocking operations, since there are magnitudes of performance differences between the two. I think the outcome will be better architected and performing applications, but that's just a hunch. Maybe Erlang and Go do concurrency right with message passing and light weight threading models, but they haven't taken the web development world by storm either. I think Go has a good chance, but time will tell.
- rdtsc 13y ago> Using synchronous I/O is only simpler if you never have to synchronize threads No, but you have to synchronize callback chains, which is a lot worse in a large system. Otherwise it is kind of a tautology, well you don't have threads so you can't synchronize threads. But one main reason to synchronize threads is to prevent shared data from being corrupted from concurrent access. But now also the business logic is split based on IO points. > Also, this might sound weird, but I think there are some benefits to forcing developers to think differently about blocking vs non-blocking operations, No doubt it is important to be aware how these things work probably down to libc level. What makes node.js tick, v8 and libuv, what do those do, and so on. > since there are magnitudes of performance differences between the two. Completely disagree. There are no general magnitudes of performance differences for all applications between those two. There are application specific and architecture specific. > I think the outcome will be better architected and performing applications, but that's just a hunch. Spreading business logic across callback boundaries or sprinkling yields or thens, is not always a good way to handle things. It is in some cases but as a general approach, I think it is pretty bad. > Maybe Erlang and Go do concurrency right with message passing and light weight threading models, but they haven't taken the web development world by storm either One reason is because people don't understand the underlying technology and its limitations or are just not aware about other programming paradigms (especially when handling distributed and concurrent issues). So I tend to not go along much with "well many are doing things this way so it must be better". Over the years I believe more that just following the "many" crowds get one an average result. Your stuff is just as broken or works just as well as anyone else. That is why it is important to look at Go, Erlang, Rust, Haskell, Prolog etc. There provide new ways of thinking that could help you accelerate faster than the rest of the crowd.