Here's a fun fact that every Amiga programmer seems to understand that Unix programmers just never really got: actual computing hardware is inherently asynchronous and interrupt-driven. The whole Amiga system is structured around using the CPU to program the custom chips and initiate DMA transfers, and installing interrupt handlers that perform the next phase of the program once the custom chips finished their work. It's how shit got done on the Amiga. By contrast, the lack of good support in early Unix for asynchronous I/O is said to be one of the reasons why X11 is such an ugly pile of hacks.
Even on Unix, if you wrote a server procedurally what you'd end up doing is having main() start a select loop that... dispatches incoming events to callbacks. All Node does really is to abstract away the select loop -- one less thing for the developer to worry about. The Node way is simpler than the conventional C way.
So no, there's nothing inherently superior about linear, procedural code, especially when such code is not at all how a computer system that must interact with the outside world works. Whether asynchronous, callback-driven code is confusing or not depends on what background you come from. Put an old-school scener in front of Node and he might form an entirely different opinion about it from OP.
I think the question the article is asking is "Why did did Node stop at abstracting the event loop away, when it could've also abstracted the execution context?" There are languages that do this - Go, Erlang, Haskell, and Python 3.4 all provide a sequential process/coroutine abstraction on top of an implementation that's fundamentally asynchronous.
The question the author has isn't about implementation, it's about programming model. Programming languages & frameworks are ultimately made for humans, and most humans find it easier to reason about "Do this thing, then this thing, then these other things, until you're finished" than "I'm doing all these things at once!" We have computers to organize all the events, stack frames, wait queues, etc. that take us from the machine to the editor window.
What I'm saying is that a programming model that's confusing to you is perfectly natural to someone else who comes from an environment where that model is all you have. People have used interrupt/event handlers and callbacks literally since the dawn of computing. It's nothing new. It just has to be learned, the way programming itself has to be learned.
Have you considered that the reason the dumb, synchronous Unix model is much more common is because it's easier for people to understand? There must be some reason that it succeeded despite being less efficient.
Comments
Here's a fun fact that every Amiga programmer seems to understand that Unix programmers just never really got: actual computing hardware is inherently asynchronous and interrupt-driven. The whole Amiga system is structured around using the CPU to program the custom chips and initiate DMA transfers, and installing interrupt handlers that perform the next phase of the program once the custom chips finished their work. It's how shit got done on the Amiga. By contrast, the lack of good support in early Unix for asynchronous I/O is said to be one of the reasons why X11 is such an ugly pile of hacks.
Even on Unix, if you wrote a server procedurally what you'd end up doing is having main() start a select loop that... dispatches incoming events to callbacks. All Node does really is to abstract away the select loop -- one less thing for the developer to worry about. The Node way is simpler than the conventional C way.
So no, there's nothing inherently superior about linear, procedural code, especially when such code is not at all how a computer system that must interact with the outside world works. Whether asynchronous, callback-driven code is confusing or not depends on what background you come from. Put an old-school scener in front of Node and he might form an entirely different opinion about it from OP.
I think the question the article is asking is "Why did did Node stop at abstracting the event loop away, when it could've also abstracted the execution context?" There are languages that do this - Go, Erlang, Haskell, and Python 3.4 all provide a sequential process/coroutine abstraction on top of an implementation that's fundamentally asynchronous.
The question the author has isn't about implementation, it's about programming model. Programming languages & frameworks are ultimately made for humans, and most humans find it easier to reason about "Do this thing, then this thing, then these other things, until you're finished" than "I'm doing all these things at once!" We have computers to organize all the events, stack frames, wait queues, etc. that take us from the machine to the editor window.
What I'm saying is that a programming model that's confusing to you is perfectly natural to someone else who comes from an environment where that model is all you have. People have used interrupt/event handlers and callbacks literally since the dawn of computing. It's nothing new. It just has to be learned, the way programming itself has to be learned.
Have you considered that the reason the dumb, synchronous Unix model is much more common is because it's easier for people to understand? There must be some reason that it succeeded despite being less efficient.
("The Rise of Worse Is Better": http://dreamsongs.com/RiseOfWorseIsBetter.html)