And that's why Python supports `asyncio` (not to be confused with `asyncore` which is obsolete). So I prefer:
sock = yield from connect('localhost', 7942)
msg = yield from read_message(sock)
print(msg)
to this callback hell: connect('localhost', 7942, function(sock) {
read_message(sock, function(msg)) {
console.log(msg);
});
});
And they're completely equivalent. Why would someone actually like the latter, except you're forced to use it because of the prevalence of JavaScript?`asyncio` is not only easier to use than the traditional "manual" state machines with classes, but also much faster than something like `Twisted`. In my crappy local benchmark[1], it easily beat `Twisted` in a considerable margin. That may be because of the flaws in my test code, but still their performance is on par. I see no points in relying on callback hell, at least in Python, except the lack of the libraries that support asyncio, which makes me a little bit sad.
Part of the reason for stagnation was because you really need new language features -- i.e. yield/send, yield from, etc. JavaScript already had the necessary language features for its concurrency model.
async read(connect, readMessage, print) {
const sock = await connect('localhost', 7942),
msg = await readMessage(sock);
print(msg);
}Lua was quite pleasant to work with in this context!
The different syntax also prevents you from creating functions that can be reused by both sync and async code.
The big thing promises and other callback-based control flow libraries do is that they fix exception handling in async code (its a PITA to add error handlers in every callback when writing async code by hand). They also let you avoid some of the awkward "pyramid of doom" nesting but thats not really killer feature because you can also achieve that by using tons of named functions.
The required bits are in place... alternatively you can use co/koa which uses generators along with yeilding promises to act in a similar fashion.. it does work pretty well, and will only get better.
Don't despair the future is bright :)
Python isn't an exception among languages that people in industry actually use.
For "production grade" server software, which is driving the newfound interest in concurrency, the following languages cover 99% of code written in the last 20 years: C/C++/Java/PHP/Perl/Python/Ruby and the Microsoft Stack (C#, VB).
None of them have anonymous closures. (C# might, but it's also newer, and it's not the the prevailing style of concurrency in any case.)
This is basically JavaScript's Scheme heritage showing through. Anonymous closures are old hat for people who went through a CS program teaching Lisp, but they're not by any means standard in industry.
One of the main reasons that none of the languages needed this feature is because the predominant paradigm for concurrency for the last 2 decades was threading, not state machines and callbacks.
Perl and PHP both have closures. In fact, Perl is closer to Scheme/Lisp than most languages (at least Perl doesn't screw up scoping like JavaScript, replace first-class functions with second-class-bastardizations like Ruby, or do whatever Python thinks it's doing with lambda... ugh)
#!/usr/bin/node
function main() {
var a = 1;
console.log(a);
// named closure
function foo() {
a = a + 1;
}
foo();
console.log(a);
// anonymous closure
(function() {
a = a + 1;
})();
console.log(a);
}
main();> There is no such thing as an "anonymous closure". There are only anonymous functions which may or may not create a closure.
Which is exactly what you did: calling an anonymous function which happens to create a closure over variable `a`.
Python has first class functions, but not anonymous functions, since lambdas aren't full functions.
And Python doesn't have closures, because you have to use "global" or "nonlocal" (in Python 3) to assign a a variable in an enclosing scope.
These features let you write concurrent state machines in JS without explicit state -- you basically use the stack as state.
Python was pretty early in terms of event loop programming for interpreted languages -- asyncore in the stdlib, and Twisted. But I agree that the node.js style is nicer than asyncore, Twisted, or Tornado. I prefer coroutines over state machines, but if you're going to write state machines, than doing it with anonymous closures is nicer than doing it "manually" with classes and so forth.
Tcl was even earlier, but Tcl is pretty foreign to most people, and was more of an embedded language than a scripting language. Pretty sure Tcl has full anonymous closures, but it is a bizarre language in some other ways.
I haven't seen the event loop style in Perl but I think you can do it. If someone has an example I'd like to see it. To me, Perl only barely qualified as a real programming language because you can't even name your function params? At least when I used it, which was over 10 years ago.