4 ms·
"Asynchronous event-driven programming is at the heart of Node.js, however it is also the root cause of Callback Hell." I'd argue the root cause is... callback
by tlrobinson 11y ago
"Asynchronous event-driven programming is at the heart of Node.js, however it is also the root cause of Callback Hell."
I'd argue the root cause is... callbacks.
Asynchronous programming can be done elegantly, in a synchronous style using "async/await", originally (?) in C# [1], likely to be added to the next version of JavaScript [2], also in Dart [2], Hack [4], and Python 3.5 [5]. It can also be emulated in languages with coroutines/generators [6][7][8] (which in turn can be implemented by a fairly simple transpiler [9][10])
This:
function foo(a, callback) {
bar(a, function(err, b) {
if (err) {
callback(err)
} else {
baz(b, function(err, c) {
if (err) {
callback(err)
} else {
// some more stuff
callback(null, d)
}
})
}
})
}
Becomes this:
async function foo() {
var a = await bar(a)
var c = await baz(b)
// some more stuff
return d;
}
And you'll see even greater improvements when using other constructs like try/catch, conditionals, loops, etc.
[1] https://msdn.microsoft.com/en-us/library/hh191443.aspx https://msdn.microsoft.com/en-us/library/hh191443.aspx
[2] http://jakearchibald.com/2014/es7-async-functions/ http://jakearchibald.com/2014/es7-async-functions/
[3] https://www.dartlang.org/articles/await-async/ https://www.dartlang.org/articles/await-async/
[4] http://docs.hhvm.com/manual/en/hack.async.asyncawait.php http://docs.hhvm.com/manual/en/hack.async.asyncawait.php
[5] https://lwn.net/Articles/643786/ https://lwn.net/Articles/643786/
[6] https://github.com/petkaantonov/bluebird/blob/master/API.md#promisecoroutinegeneratorfunction-generatorfunction---function https://github.com/petkaantonov/bluebird/blob/master/API.md#...
[7] https://github.com/kriskowal/q/tree/v1/examples/async-generators https://github.com/kriskowal/q/tree/v1/examples/async-genera...
[8] http://taskjs.org/ http://taskjs.org/
[9] https://babeljs.io/docs/learn-es6/#generators https://babeljs.io/docs/learn-es6/#generators
[10] https://facebook.github.io/regenerator/ https://facebook.github.io/regenerator/
- tracker1 11y agoNot only the simplified code you write with async/await (using babeljs for this today) ... There are a lot of complex processes that are easier to reason against in JS over low level C. Not to mention, that the article's example is only comparing a small piece of work... A single node instance can easily manage 10K simultaneous requests... launching a thread per request, and you're going to hit resource bottlenecks at the CPU pretty quickly, compared to several node instances approaching a million simultaneous connections on a single server. Node uses not only an even loop, but a shared thread pool for isolation of work without blowing out resource contention. I've seen issues with even a few thousand threads in poorly written simulation servers... going to an event-loop against a threadpool always worked out better under true load.
- sre_ops 11y agoChapter and verse of this marvelous tale? [Github link] to something that I can pull, throw at it 5k RPS, make it proxy a result from Google?
- rjrodger 11y agoCallbacks suck, no doubt, and the sooner we have an real alternative (sorry Promises, not you) for them in JS, the better. In the meantime, for those of us stuck with contemporary JS, there are mitigating code styles, in particular "early return": function foo(a, callback) { bar(a, function(err, b) { if (err) return callback(err) baz(b, function(err, c) { if (err) return callback(err) // some more stuff callback(null, d) }) }) } Just to be clear, this is still inferior to a linear style. It is however much clearer than the "traditional" style.
- phs2501 11y agoOK, I'll shoot: Why not Promises? They don't handle streaming data, but they're not supposed to; they're supposed to model the traditional JS function call model (single value return or single value exception) which they do well. For streaming data use some other paradigm (perhaps Observables from https://github.com/jhusain/asyncgenerator https://github.com/jhusain/asyncgenerator), but that's orthogonal to Promises, not a replacement for them. The only other reasonable complaint I've heard is that they aren't cancellable, which is legit. There are proposals for that, though (https://github.com/petkaantonov/bluebird/blob/master/API.md#cancellation https://github.com/petkaantonov/bluebird/blob/master/API.md#...). At least with Promises functions actually return, as opposed to the continuation-passing-style of callbacks. (CPS is a fine intermediate representation for compilers, but it really shouldn't be written by hand; that's why we have compilers!)
- jameslk 11y agoNot the op, but I can give you one important reason for me why Promises aren't as great when compared to callbacks (specifically wrt the ES7 async/await syntax): with callbacks, I have greater flexibility in choosing the best approach for running asynchronous code in parallel. For example, let's take the wonderful async.js library. There are a variety of different tools at my disposal on picking how to run tasks in parallel. My favorite one is async.auto. With this method, I can define the asynchronous tasks and their dependencies, then async.auto finds the best way to run the tasks in parallel while still ensuring the tasks end up having their dependencies met. This is more in tune to declarative programming's ideal of defining what results you want, rather than specifying how to accomplish getting those results. Now with ES7's async/await, everything is still sequential. Better than blocking synchronous programming, but it could be better.