4 ms·
I think this is a fantastic article in many ways. It clearly describes the issues of callback hell and gives simple examples on how to clean it up. Modern Java
by spcoder 10y ago
I think this is a fantastic article in many ways. It clearly describes the issues of callback hell and gives simple examples on how to clean it up.
Modern JavaScript has gotten very complicated lately because it's so flexible. Many people are developing interesting frameworks to solve niche problems; however, it feels like many of these solutions are overly complicated outside the niche. Yet, developers are adopting these frameworks due to it being trendy instead of choosing the correct tool for the job.
Many times, the correct "tool" for the job can be a mixture of an effective standard library, small specific libraries, and good conventions like the author describes.
I've been a programmer for 18 years now, and I am convinced that simple proven solutions are the way to go.
- mediocrejoker 10y agoMaybe 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.
- jimbokun 10y ago"I've been a programmer for 18 years now, and I am convinced that simple proven solutions are the way to go." I would rephrase this slightly to "simple proven solutions that solve the immediate problem at hand, and nothing else." Bringing in lots of extraneous dependencies, or DSLs offering functionality way beyond whats actually needed for the immediate problem at hand, can quickly lead to code that is very hard to understand, maintain, and debug.