4 ms·
Maybe it's because I'm a C programmer and not familiar with js but I felt this article did a very poor job of describing "callback hell." I still don't have a c
by mediocrejoker 10y ago
Maybe it's because I'm a C programmer and not familiar with js but I felt this article did a very poor job of describing "callback hell." I still don't have a clear idea of what it is.
The author shows some code with lots of nested if/else clauses and claims this is bad because it blocks; fair enough. Then they explain what callbacks are and that some people have trouble understanding the asynchronous nature.
Then they immediately go into an explanation called 'How do I fix callback hell?' and meanwhile I'm scrolling up to see if I've missed something.
Perhaps people unfamiliar with this particular problem are not the target of this article, but that doesn't really make sense to me.
- neogodless 10y agoI believe the primary issues are a mistaken belief that "every line of code that gets executed should be read from top to bottom in the same order" (an issue with any code that isn't modular) and the debugging issue of using anonymous functions defined inline. Readability of code is very important. Inline callback definitions hurt this.
- danneu 10y agoExtracting inline callbacks leads to indirection, especially if you're only extracting to avoid indentation. It doesn't make the code simpler or easier to follow. Promises and especially async/await let you read code top to bottom once more, with other benefits like monad chains that catch synchronous failure.
- neogodless 10y agoAny one section of code should have a "need to know" format. While you want to know "what's happening" in order of execution, having all the "how it's happening" code shouldn't be necessary. For example, you want to know that the code is about to "getAccountData" but you shouldn't need to know how. You see that it is called, and you see that afterward, a callback called "loadAccountDataInReport" will be called. Why should you see all the guts of those two functions inline?
- danneu 10y agoNow let's say you're in a route handler where the buck stops, and you need to compose any number of async calls, some branching on if-else logic and/or the results of other async calls. This is where the "just wrap it" argument breaks down. Though I think promises are only slightly better when you have if-else branches that add to the async chain. It's not til async/await where Javascript finally hits its stride.
- jdormit 10y ago> See the pyramid shape and all the }) at the end? Eek! This is affectionately known as callback hell. In other words, "callback hell" is simply the state of having too many nested inline callbacks. It makes the code uglier and for some harder to read. That's why pulling callbacks out into their own functions (i.e., making them no longer inline) fixes callback hell.
- mhink 10y agoHmm. Lemme see if I can explain this. Javascript uses "function references" in a similar fashion to C's "function pointers". This... function fooAllTheBars() { /* ...quux... */ } ...is a function declaration statement which binds a callable reference to my function to the name `fooAllTheBars`. That said, we can express an "anonymous function declaration" as an rvalue, so we can also bind the function to a name ourselves: const fooAllTheBars = function() { /* ...quux... */ }; Since this is a function reference, I could pass it into a function like this: setTimeout(fooAlltheBars, 1000); ...which would tell the JS runtime to wait 1000ms and then execute `fooAllTheBars`. I could also express this using an anonymous function: setTimeout(function() { /* ...quux... */ }, 1000); This is all fine and dandy, but suppose I also had a function like this: function quuxify(value, calculator, cb) { calculator(value, function(err, calcResult) { if (!err) { cb(calcResult.toFixed(2)); } }); } function bazzify(success, failure) { quuxify('foo', function(value, cb) { setTimeout(function() { if (value > 100) { cb("too high!", null); } else { cb(null, value - 10); } }, 1000); }, success); } bazzify(console.log, () => {}); This is, admittedly, a bit of a pathological example than the simple "nested callback problem", and the code makes even less sense because "bazzify" and "quuxify" don't actually mean anything, but I think it's a better illustration of the problem at hand. Basically, if you get into callback hell, your control flow starts bouncing around like a pinball hitting bumpers, and figuring out why is nearly impossible (unless you want to dig through fifteen levels of anonymous functions). And the sad part is that in most cases, you can't avoid it altogether because there's something fundamentally asynchronous like a `setTimeout` or an `xmlHttpRequest` waiting at the end of the call stack.