It seems a bit cruel that he mentions "horror stories" about Twisted; most of the culture shock people complain about with Twisted is exactly the kind of flow-control shenanigans that he describes in Node.js. In fact, Twisted makes those particular examples easier.
To handle branching flow-control like 'if' statements, Twisted gives you the Deferred object[1], which is basically a data structure that represents what your call stack would look like in a synchronous environment. For example, his example would look something like this, with a hypothetical JS port:
d = asynchronousCache.get("id:3244"); // returns a Deferred
d.addCallback(function (result) {
if (result == null) {
return asynchronousDB.query("SELECT * from something WHERE id = 3244");
} else {
return result;
}
});
d.addCallback(function (result) {
// Do various stuff with myThing here
});
Not quite as elegant as the original synchronous version, but much tidier than banging raw callbacks together - and more composable. Deferred also has a .addErrback() method that corresponds to try/catch in synchronous code, so asynchronous error-handling is just as easy.
For the second issue raised, about asynchronous behaviour in loops, Twisted supplies the DeferredList - if you give it a list (an Array, in JS) of Deferreds, it will call your callback function when all of them have either produced a result or raised an exception - and give you the results in the same order as the original list you passed in.
It is a source of endless frustration to me that despite Twisted having an excellent abstraction for dealing with asynchronous control-flow (one that would be even better with JavaScript's ability to support multi-statement lambda functions), JavaScript frameworks generally continue to struggle along with raw callbacks. Even the frameworks that do support some kind of Deferred or Promise object generally miss some of the finer details. For example, jQuery's Deferred is inferior to Twisted's Deferred: http://article.gmane.org/gmane.comp.python.twisted/22891
The differences between your example and the common JavaScript practice for promises (when they're used; most of the time they aren't) are that then is used instead of addCallback and that chaining is available and taken advantage of.
Ah, yes. Twisted's Deferreds do support that kind of chaining, but I didn't use it in my original snippet because I didn't want to have an example of a Deferred where no Deferreds were actually visible. :)
In my own code, I tend not to use chaining because "methods returning self" is not a common idiom in Python (although tools like jQuery have given it currency in the JS world) and because I haven't yet figured out a way of formatting a multi-line method invocation that doesn't look messy.
For starters since it event based nothing can happen until you give back control to event loop. Secondly if you add another callback or errback to a Deferred that has already been fired it just calls it with the last result. Note, Deferred is much simpler than I imagine you are thinking it is. It does nothing to handle events it is just a first class way to represent the flow of code.
To handle branching flow-control like 'if' statements, Twisted gives you the Deferred object[1], which is basically a data structure that represents what your call stack would look like in a synchronous environment. For example, his example would look something like this, with a hypothetical JS port:
Not quite as elegant as the original synchronous version, but much tidier than banging raw callbacks together - and more composable. Deferred also has a .addErrback() method that corresponds to try/catch in synchronous code, so asynchronous error-handling is just as easy.For the second issue raised, about asynchronous behaviour in loops, Twisted supplies the DeferredList - if you give it a list (an Array, in JS) of Deferreds, it will call your callback function when all of them have either produced a result or raised an exception - and give you the results in the same order as the original list you passed in.
It is a source of endless frustration to me that despite Twisted having an excellent abstraction for dealing with asynchronous control-flow (one that would be even better with JavaScript's ability to support multi-statement lambda functions), JavaScript frameworks generally continue to struggle along with raw callbacks. Even the frameworks that do support some kind of Deferred or Promise object generally miss some of the finer details. For example, jQuery's Deferred is inferior to Twisted's Deferred: http://article.gmane.org/gmane.comp.python.twisted/22891
[1]: http://twistedmatrix.com/documents/current/core/howto/defer....