This is a good, simple explanation of how event-based servers work.
However, I strongly disagree with the "good code / bad code" dichotomy the author presents. Asynchronous, callback-based code is frequently more difficult to write, harder to reason with, more inflexible, and buggier than similar synchronous code. That said, moving to event-based code has generally been worth the cost for us, yielding improvements in performance and reliability that make up for the increase in developer time required.
The problem is that it's often very difficult to refactor synchronous code to be event-based since it requires an inversion of control. An attempt at changing clear, intention-revealing synchronous code to work asynchronously frequently results in ugly, hard-to-follow callback hell. Worse, a single piece of blocking code left inside the event loop can now render all of the work you've done moot, meaning such refactorings tend to sprawl.
This is why I think attempts to bridge this gap, making asynchronous read more-or-less the same way as asynchronous code, are very valuable. Scala's delimited continuations, coming in the upcoming 2.8 (http://www.scala-lang.org/node/2096), could help take a lot of the pain out of using event-based servers effectively.
>The problem is that it's often very difficult to refactor synchronous code to be event-based since it requires an inversion of control. An attempt at changing clear, intention-revealing synchronous code to work asynchronously frequently results in ugly, hard-to-follow callback hell.
This is true. There's some merit to the argument that you should plan for your IO code to be asynchronous from the beginning and structure your program that way.
> Asynchronous, callback-based code is frequently more difficult to write, harder to reason with, more inflexible, and buggier than similar synchronous code.
That is an excellent point that is usually ignored. Thread per request is much easier to understand and work with. Of course, thread per request is simply not a good choice in cases like a server with support for web sockets, but it is good enough in many cases. My experience has mainly been with Java (NIO vs. simple threads and Executors). Paul Tyma has made a more convincing argument than me about this -
The good code / bad code thing is a bit out of context in my slides - what I meant is that if you're using an event loop any blocking function call will stall your event loop and cause all of your other clients to pause. I agree that this style of programming is a lot less clear, although I find that JavaScript's anonymous function syntax makes it less painless than most.
However, I strongly disagree with the "good code / bad code" dichotomy the author presents. Asynchronous, callback-based code is frequently more difficult to write, harder to reason with, more inflexible, and buggier than similar synchronous code. That said, moving to event-based code has generally been worth the cost for us, yielding improvements in performance and reliability that make up for the increase in developer time required.
The problem is that it's often very difficult to refactor synchronous code to be event-based since it requires an inversion of control. An attempt at changing clear, intention-revealing synchronous code to work asynchronously frequently results in ugly, hard-to-follow callback hell. Worse, a single piece of blocking code left inside the event loop can now render all of the work you've done moot, meaning such refactorings tend to sprawl.
This is why I think attempts to bridge this gap, making asynchronous read more-or-less the same way as asynchronous code, are very valuable. Scala's delimited continuations, coming in the upcoming 2.8 (http://www.scala-lang.org/node/2096), could help take a lot of the pain out of using event-based servers effectively.