20 ms·
Callback Hell (2016)
- realPubkey 10y agoA website? Really? Why not just write an article on medium?
- wingerlang 10y agoThis website was created before Medium even existed.
- deleted 10y ago[deleted]
- java_576 10y agoI prefer to own the URLs to my blog posts so that if I want to monetize my site I have control over it.
- realharo 10y agoLast updated in 2012. A lot has changed since.
- kraftman 10y agoCould you elaborate? Coming from Lua I always found the nesting of anonymous functions messy and wondered why they weren't done as in the article.
- awjr 10y agoPromises and, more recently, Observables. Simply put: A promise resolves to a single value asynchronously, an observable resolves to (or emits) multiple values asynchronously (over time). Taken from here: http://stackoverflow.com/questions/36064303/what-are-the-differences-between-observables-and-promises-in-javascript http://stackoverflow.com/questions/36064303/what-are-the-dif...
- awjr 10y agoOh I also forgot async/await "The purpose of async/await functions are to simplify the behavior of using promises synchronously and to perform some behavior on a group of Promises. Just like Promises are similar to structured callbacks, async/await is similar to combining generators and promises." https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- Kiro 10y agoPromises and async/await. This page is so outdated it shouldn't exist.
- mromanuk 10y agoHe addressed more modern solutions at the bottom. I think, he is trying to evangelize "you should write readable code, no matter of the limitations of the language."
- insertnickname 10y agoThe page does mention those.
- awjr 10y agoTBH it shouldn't be on the front page of HN.
- realharo 10y agoYou can now use async/await directly in node (no transpiler necessary), and both Babel and TypeScript can compile them for browser use. Also there are now many more libraries that use native ES6 promises. Before: function getPhoto(callback) { someCustomHttpGet("/favorite_photo_id", function(err, data) { if (err) { return callback(err) } try { var dataJson = JSON.parse(data) someCustomHttpGet(dataJson.url, function(err, data) { if (err) { return callback(err) } callback(undefined, processData(data)) }) }catch(e) { callback(e) } }) } After: async function getPhoto() { const data = await fetch("/favorite_photo_id") const dataJson = await data.json() const photoData = await fetch(dataJson.url) return processData(photoData) } The example may be a bit contrived, but you get the idea.
- camus2 10y agoYour try catch is useless here, AFAIK JSON.stringify doesn't throw. and you can't catch someCustomHttpGet() "exceptions" if the latter is supposed to be asynchronous. relevant: http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
- realharo 10y agoYeah, I messed it up when quickly typing the code without thinking, it should've been JSON.parse, I fixed it now. Also regarding that article: you can call async functions from non-async functions - you will simply get the Promise object instead of the data. If you're using TypeScript, your editor will immediately highlight the error if you try to use that Promise object as though it were the data, so zero chance of getting it wrong. Try it yourself - https://goo.gl/vDk3Gz https://goo.gl/vDk3Gz - hover over the `n` inside the `formatName` call.
- camus2 10y ago> Also regarding that article: you can call async functions from non-async functions - you will simply get the Promise object instead of the data. async function() getZ(){ return promise } function() getX(){ return x = y * getZ() } // sync call globally. let x = getX(); // NaN async/await makes things more readable, it doesn't change the issue of leaky abstraction and you need to know whether the call is blocking or non blocking when calling getZ and using a function that calls getZ. What should you do? assume everything is asynchronous by default? Imagine you have a validation lib, you don't need it to be asynchronous to validate a string of length > 10, but you need to rewrite your whole API if you need to validate the fact a unique index has to be validated from a database.
- colinramsay 10y agoThis is mentioned in the last section of the article: "What about promises/generators/ES6 etc?"
- realharo 10y agoThere was a lot less support for them in the ecosystem back then, so you had to make certain trade-offs that you no longer need to make today.
- tomhoward 10y agoThe GitHub repo was last updated in 2012, but the site has much more recent updates, as recent as 2016 (with a reference to ES7).
- lucideer 10y ago> In other languages like C, Ruby or Python there is the expectation that whatever happens on line 1 will finish before the code on line 2 starts running and so on down the file. As you will learn, JavaScript is different. A lot of beginner guides to various programming languages make this mistake of associating a certain property with a language as if it's inherent. In this case, async code - while sold as a main feature of the NodeJS platform - is in no way exclusive to, or even an inherent part of, Javascript/ES the language. This may seem a nitpick, but I think it's an incredibly important distinction for beginners (or at least, I think it's incredibly important not to mislead beginners into believing in this limitation early on). Another common example was, up until recently, that Javascript "wasn't powerful enough" to do filesystem access, hardware operations, etc. A simple side-effect of the environment the language was most commonly executing in (the browser) was turned into an inherent limitation of the language in order to "simplify things for beginners". While bombarding beginners with a lot of info at the start is a bad idea, these kind of misconceptions can be very damaging. They leads to a very narrow idea of what's possible with (any) languages in general, and uninformed decisions on what to learn as a result.
- masklinn 10y ago> A lot of beginner guides to various programming languages make this mistake of associating a certain property with a language as if it's inherent. In this case, async code - while sold as a main feature of the NodeJS platform - is in no way exclusive to, or even an inherent part of, Javascript/ES the language. Exclusive no, but considering JS's original environment and constraints it is in fact pretty inherent. `setTimeout` is part of the core language and has been for a long time, a blocking `sleep` is not.
- bjarneh 10y agoNot sure this problem goes away by giving names to anonymous functions, which seems to be his suggestion
- williamdclt 10y agoNop, but it can be a bit better by using predefined functions rather than inline ones.
- bjarneh 10y agoProbably better, but the callback hell stems from a broken up control flow; where we cannot read / understand what's going on with a linear (top to bottom) scan of the source code (even for small sections of code). I think programming where we have state and things can happen at any time, where programs wait for events or something similar has proven to be difficult to write/understand/maintain. The 30+ years it has taken to write the (unfinished) Herd micro-kernel is a good example.
- cmancini 10y agoAgreed. It tags the complexity with identifiers, which is of some use, but it doesn't change anything about the execution path which can still get wildly out of hand.
- increment_i 10y agoAs a curious and perhaps naive aside, I've been wondering why the programmer should need to care about asynchronous execution of code at all. Can't it all be abstracted under a procedural layer and let the OS worry about not blocking anything? The advent of promises, async.js, and other paradigms tell me that people still kind of want to write code that does one thing after another, then another, then another.
- kraftman 10y agoOpenresty does this quite nicely with Lua: you just write your code as normal and it handles the rest in the background. http://leafo.net/posts/itchio-and-coroutines.html http://leafo.net/posts/itchio-and-coroutines.html
- klibertp 10y agoIo language does the same. The problem is that most languages are not powerful enough to easily express this. You basically need call/cc in some form or built-in coroutines. And a library/framework designed for this.
- romanovcode 10y ago>people still kind of want to write code that does one thing after another, then another, then another. For the most part - yes. And not because they want it but because there is no other way. You usually need to do something before you do that next thing anyway.
- insertnickname 10y agoThat's how it's done in Go. You write plain, "blocking" code, and if it blocks, the Go runtime will just schedule another goroutine (green thread). In Go there's no need for callbacks or littering your code with `await` keywords to write concurrent software.
- tluyben2 10y agoI never checked that out but ; https://www.golang-book.com/books/intro/10 https://www.golang-book.com/books/intro/10 looks messy to me. Less messy than callbacks but more messy than async/await imho. Maybe there are nicer examples? Also I agree with the coroutine LUA rationale you can read in the link in this same thread. It appears that with Go I still need to alter my own code to use libraries that are written to run async or am I wrong?
- williamdclt 10y agoLot of blabbling for a simple concept: do not abuse nesting. It's the case with control statements, it's still the case with functions, and even more with async functions. Oh, and callbacks definitely exist in other languages, like C. What's the deal with this trend of setting up a whole website for a (basic) blog post?
- briceb8e 10y agoSEO.
- meowpants 10y agoDon't be hatin'. It's a useful read for many. "Simple concept" is subjective. I'd bet that discussing "simple concepts" with an engineer or scientist would likely cause your mind to grasp and instinctively try to relate to your own memorization of facts. But that shouldn't preclude you from discussing what you do know, or for that matter, sharing it with the world.
- williamdclt 10y ago(answering you, but also for otherz saying the same thing below) Yeah you're probably right, I've been a bit pedantic. I feel this article is unnecessary long for the subject, it would be a good length for something like "setting up a hadoop cluster", but having all sorts of articles about all subjects is a very good thing. My bad.
- davidrm 10y agoDetailed posts like these help beginners.
- emdowling 10y agoI would've loved an article this well written and explicit when I was first learning JS (or even any language, really). Really well done with good, practical examples.
- delinka 10y agoCallbacks exist in C. But inline functions (where a parameter to another function would be placed), don't. Actually, we don't even have closures in standard C (there is a non-standard extension provided by Apple that maybe you can use to recreate JavaScript's callback hell if you're so inclined...)
- paradite 10y agoThis problem has been addressed (in terms of proper coding style at least) long time ago with async.js: http://caolan.github.io/async/ http://caolan.github.io/async/ https://github.com/caolan/async https://github.com/caolan/async
- zump 10y agoAs a C programmer working with a large codebase, I have come to HATE callbacks. Seriously, the worst feeling ever is tracing through a huge function tree, only to run into function pointer dereference. Then you have to go on a wild goose chase to find out when, where and what it will be assigned to. STATIC TYPES PEOPLE.
- MrBuddyCasino 10y agoAs a C beginner I don't like them either. But are there techniques to avoid them? Take for instance nghttp2 which needs callbacks to handle I/O.
- pwdisswordfish 10y agoWhat does it have to do with static typing? Function pointers are perfectly valid types. Also, can't you just use a debugger?
- dahart 10y agoC/C++ callbacks are fairly different in practice than what the article is talking about. You might like callbacks in JavaScript. You might really like promises -- no wild goose chase to track down the chain, because promises chain things explicitly and make async code look more synchronous. What's most interesting about your point is that what the article suggests -- naming and de-nesting callbacks -- is in a way what you're warning against. By naming it and physically moving it, execution that was contained within a single block of code is now jumping around, and can move to other places where you have to go looking. Even though it's considered an anti-pattern for good reasons, there are some advantages to nested callbacks.
- dorianm 10y agoSame code than first example as synchronous ruby: https://gist.github.com/Dorian/91b965989c1f8f1c727b49d66477ade5 https://gist.github.com/Dorian/91b965989c1f8f1c727b49d66477a... (Add `begin; rescue; end` where errors need to be handled)
- anc84 10y agoI guess I should register abusingjavascriptfortriviallayoutinghell.com for this? http://i.imgur.com/JMzoOmv.png http://i.imgur.com/JMzoOmv.png
- zappo2938 10y agoI enjoy using promises.
- j_r_f 10y agoSame. This seems to be an unpopular opinion on HN though.
- jrochkind1 10y agoreally? Weird. I love promises. What do people have against them?
- thedjinn 10y agoAfter working with React Native for a while now I really began to appreciate the usefulness of async/await. By expressing asynchronous operations in a more linear fashion it becomes much easier to reason about them.
- chukye 10y agoI really don't see any problem with callbacks.
- wildpeaks 10y agoAs much as I enjoy async/await, the filesize penalty once Babel-ified can bloat up your bundles; better stick to Promises-only for now, even if it's slightly more verbose. However they can already be used in Node with the "--harmony-async-await" flag (and without flag in the upcoming Node 8).
- dpweb 10y agoThe 'asynchonous problem' in JS is twofold. It makes it more difficult to reason about the program, and is hell on readability, and readbility matters. Support, maintenance, etc. The lack of an ability to structure async ops in a simple way is the biggest drawback in the language. Callbacks of course have the drawbacks mentioned. Sure, you can separate the functions, smaller functions and that too is preferred, but still you get code that can be difficult to reason about and read. Promises, for all the hype, in my view clearly did not help matters much. Promise code can be as weird looking or worse than callback code. Promises did not solve the problem. Async/await IS the solution. I've started converting callback and promise code to it. It's a great new enhancement.
- woah 10y agoI used to think that promises were pretty bad, but then i started using async/await. Now I think of promises as async/await with a lot of extra cruft. I have a colleague who prefers promises because they force programmers to break logic into small functions.
- foreigner 10y agoI can't wait for async/await to be added to node is proper!
- minitech 10y agoPromises did solve the problem; async/await is just syntactic sugar for them. The important thing is being able to start a task and pass around its value on completion without worrying whether that completion has happened yet. If you didn’t see an advantage to promises, you might have just been using them like callbacks.
- ufo 10y agoPromises are just as bad as callbacks in most of the code organization issues and I agree with you that they don't actually solve "callback hell". However, one thing that promises (and other library-based solutions) do better than hand-written callbacks is exception handling. By default, promises will capture and propagate and exceptions thrown inside them, which is something that is very easy to forget to do with hand-written callbacks.
- cookiesboxcar 10y agoSerious question: what is considered async? From the site, I get that add(1+1) doesn't require a callback, whereas downloadFile(url) does. What about somethingHarderThanAddition(noExternalDependency)? My question is, what is the threshold that I can assume that my code will run sync, and when it cant?
- coldtea 10y ago>My question is, what is the threshold that I can assume that my code will run sync, and when it cant? It will always run sync, unless you call something that accepts a callback (either at the first level, or nested in something else you call).
- SEMW 10y ago> what is the threshold that I can assume that my code will run sync, and when it cant? Generally you won't have a choice. If something in your function calls another async function, the function as a whole is forced to be async. If not, then it won't be. It's possible to make a function fake-async even if it doesn't call any async functions, by just calling the callback at the end. But there's not much point -- and callers may make assumptions about the the callback being called in a separate iteration of the event loop, so doing that could cause subtle bugs. That latter problem is solvable by calling the callback in a setImmediate callback, but again, no point unless you have a good reason to. (Exception: there are a few apis where you do have a choice, e.g node's randomBytes can be either sync or async. Generally they're things that might block, where the sync version returns an error if it can't satisfy immediately. But generally you don't.)
- kazagistar 10y agoAnything that suspends processing and waits for the OS to provide some data (file, time, socket, or piping usually) should be behind a callback. Beyond that benefit is more limited (in a single threaded app like NodeJS), since you are no longer using async to do othe stuff while you wait for resources. However, if the computation locks up the thread for too long, it cannot service callbacks, and so timers might be delayed, heartbeat messages might be skipped leading to dropped connections, and other bad stuff. If that's the case you want to either split your large computation into many smaller ones (allowing other callbacks to fire in between) or offload the computation into another process or thread. In other words, it's an engineering decision, based on the expected consequences of the overall design in the context of understanding how the event reactor works.
- matthewaveryusa 10y ago>Let's give em names! The key piece of advice. It helps tremendously with backtraces if your program crashes.
- aljones 10y agoAlso, generally good advice for everything and something to keep in mind when designing systems. Excel has one of the worst cultures when it comes to naming things. Easy to blame the users, but tools end up being used the way their design encourages. Excel gives you meaningless names. So most things have meaningless names.
- AgentME 10y agoDo your stacktraces not have line numbers? I see this advice repeated so often and it always surprises me.
- bluetwo 10y agoI agree with the structure the author is putting out there and have been using it myself. It was honestly a mystery whey people would use this hard to read nested structure when you can write things in a more readable way. The best theory I can come up with is that it is easier to write that way if, for instance, you are answering a question on StackOverflow, and these examples just find their way into production code via copy/paste without people understanding how they work.
- pka 10y agoCont, the mother of all monads [0] :) [0] http://blog.sigfpe.com/2008/12/mother-of-all-monads.html?m=1 http://blog.sigfpe.com/2008/12/mother-of-all-monads.html?m=1
- marcosdumay 10y agoThis is the one of the many things that are anti-features in most languages, but become powerful best-practice tools when applied to Haskell.
- agentgt 10y agoI don't consider myself a huge code style pusher but when I see two space indentation I have to really ask... seriously... why???!!! Its funny to me because the languages where you get massive nesting you almost want greater indentation as it gets extremely confusing as to what scope you are in. While I prefer either 4 spaces or tabs (obviously not mixed) I'm not completely revolted with 2 spaces in Algo languages like Java, C or Go (although I suppose Go you have no choice) because you just don't have much nesting. But Scala and Javascript.... wholly crap is there a lot of nesting. Please for god sakes if you absolutely need that much indenting either refactor or give into the glorious evil that is TAB. In all serious there is an accessibility thing with 2 spaces. You are hindering people with bad eye sight. I just don't have the visual acuity anymore to discern two spaces easily and I imagine there are many more. It is sort of like building a building and making the stairs extremely steep because you don't like taking lots of steps with your long legs.
- ue_ 10y agoI started using two spaces because it was the default with C on emacs. I took a liking to it.
- api 10y agoI'm a tab heretic for this reason -- every user can adjust their editor to show tabs with whatever width they like.
- d--b 10y agoIt's because when you use 4 spaces, the lines just become too long. I use 4 spaces in C and 2 in javascript and that makes sense visually for me.
- agentgt 10y agoLet me guess... It is hard for you to read long lines or your monitor isn't big enough? You might call that an accessibility thing right? You get what I'm getting at. You traded one accessibility that is fairly easy to fix (at worse case you have line wrapping) for one that is almost impossible to fix (zooming and large font don't really help).
- mybrid 10y agocallback, n: 1. a function passed as a parameter. asynchronous programming, n: 1. code written to manage interrupts, often called signals. This article is exemplary of why any educated and trained developer will not go anywhere near the Javascript community.
- dang 10y agoPlease don't do the programming flamewar thing on HN.
- d--b 10y agoCallback hell is due to everything being async in javascript. Naming functions and handling errors is how you may get by a little better. For instance every database call is async, so for an operation that requires 3 chained calls, you'll have to nest these callbacks together.
- Jgrubb 10y agoI was looking through the vue.js documentation last night and noticed a similar code style as the one this uses. Are we back to "semi-colons are uncool"? It's so much eaier for me to glean the intent when browsing JS written with semi-colons.
- sratner 10y agoThis looks kinda like http://standardjs.com/ http://standardjs.com/, but not quite.
- spcoder 10y agoI 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.
- coldtea 10y ago>Personally I use callbacks for 90% of the async code I stopped reading right there (well, it's near the end of the article anyway, and I did continue, but you get my point).
- athenot 10y agoThis is where syntactic sugar can be helpful. CoffeeScript can make the example a lot more readable by removing the unnecessary braketing when the last argument of a function is a callback (common case in node.js). The only thing that's still ugly is the .bind() applied to a function, which somewhat messes with the style. Other than that, this makes callbacks flow a lot more naturally when reading source code. (Of course, as it is for all stylistic considerations, it's a matter of taste, not absolutes.) fs.readdir source, (err, files) -> if err console.log 'Error finding files: ' + err else files.forEach (filename, fileIndex) -> console.log filename gm(source + filename).size (err, values) -> if err console.log 'Error identifying file size: ' + err else console.log filename + ' : ' + values aspect = values.width / values.height widths .forEach ((width, widthIndex) -> height = Math.round(width / aspect) console.log "resizing #{filename} to #{height}x#{height}" this.resize width, height .write "#{dest}w#{width}_#{filename}", (err) -> if err then console.log 'Error writing file: ' + err ).bind this
- Mithaldu 10y agoPlease don't perpetuate the else silliness of the (intentionally bad) first example there. Stick a return in front of the log calls, lose the else, and unindent all following.
- athenot 10y agoYes, that is of course preferable. There are much better error handling strategies than what the example gave. I was only commenting on the readability aspect, all other things being equal.
- susw 10y agoUse '=>' instead of '->' to automatically bind 'this'.
- weberc2 10y agoI absolutely disagree. Brackets are a big part of the reason the JS version is more readable than the CoffeeScript version, IMO. I don't understand why anyone would think readability would increase with syntactical ambiguity. This isn't even the traditional braces-vs-indentation argument either; I much prefer Python's syntax to CoffeeScript's, and I use both regularly.
- intrasight 10y agoMy brain couldn't parse due to lack of semi-colons.
- shoover 10y agoThese techniques seem to help code cleanliness in the small without attacking the root issue. Names and error handling help, but modularization requires more care than is demonstrated. The new module example separates the boilerplate, yes, but it also hides document selectors that assume page structure. So it's not actually modular. It's not reusable and it's brittle for the one page it was written for (if you change the markup and don't remember to change the module, the page breaks). It needs a way to pass in the elements queried in callbacks.
- weberc2 10y agoIt's interesting that JS is still trying to figure out ways to make callback-based asynchronicity less-painful while Go managed to skirt the issue altogether. Go is readable, asynchronous and parallelizable largely without callbacks of any kind. Of course Go has its own problems, but I think they're largely orthogonal to its async story (i.e., JS could adopt Go's async strategy without adopting Go's more controversial features).
- addicted 10y agoJS became a language it was never meant to be not because of its merits, but because it is the language of the web. Go is the language it was always meant to be, and a significant part of that was handling async. Once async/await comes along, JS will have a really nice solution, and to be honest, I think it's pretty impressive that JS, with the history of its development, has been able to acquire new technologies of this nature.
- weberc2 10y agoAsync/await will be an improvement to be sure, but I think it still leaves a lot to be desired. In particular, I'm not a fan of bolting (a)synchronicity onto the function definition; instead, it's nice to be able to run a function in either a sync or async context. But yes, props to JS for continuing to improve.
- nickik 10y agoClojureScript compiles to JavaScript and you can have the exact same CPS style programming, with the exception of missing parallelism of course. Async/await are not really what you want either. There is no fundamental reason JS could not have added a CSP model.
- coding123 10y agoTypescript and RXJS (with ts mappings) make CB hell much more enjoyable. There is a learning curve to RX, but it's worth it.
- fptoperation 10y agoThe only remaining solution to the callback hell http://rawgit.com/peuler/meowmeow/gh-pages/index.html http://rawgit.com/peuler/meowmeow/gh-pages/index.html
- EdSharkey 10y agoIt's not "callback hell", it's The Pyramid of Doooooooooooom! Much more dramatic and foreboding. ;-)
- deleted 10y ago[deleted]
- turdnagel 10y agoHow about `async.waterfall`? It allows you to keep code relatively neat and execute in visual order. It's the equivalent of a `Promise.then()` chain.
- crudbug 10y agoI feel this async computational burden has been passed on to the developers without any abstraction, making the life of the foundational platform developers easier. IMO, platform IO libraries should support async behavior natively. Joe Armstrong [0] explains this beautifully. [0] http://joearms.github.io/2013/04/02/Red-and-Green-Callbacks.html http://joearms.github.io/2013/04/02/Red-and-Green-Callbacks....
- Normal_gaussian 10y ago> I don't know how you would write this in Javascript. I've written quite a lot of jQuery and know how to setup and remove callbacks. But what happens if an event is triggered in the time interval between removing an event handler and adding a new one. I have no idea, and life is too short to find out. If you are manipulating inside a single function call it is atomic, JS is single threaded and doesn't interrupt functions.
- deleted 10y ago[deleted]
- alanbernstein 10y agoCallback hell has confused me for a long time. I kept assuming there was a good reason people write code that way, and there was something wrong with naming my functions and passing them by name. The latter approach always made more sense to me - it's more readable, and more intuitive. If there is no real benefit to the nested anonymous function style, why do people do it that way? It would never even have occurred to me to write code like that if I didn't see it in javascript all over the place.
- EvilTerran 10y agoI can think of one advantage - lexical closure: when your callback's body is defined inside the calling scope, you can use variables from that scope inside the callback; and the callback can assign to those variables & have the change persist in the caller (only really relevant when the callback's invoked synchronously, but can be very useful in those cases - eg, with Array.prototype.forEach). It is possible to replicate these effects without closures, but it can get pretty messy: the former by prepending the variables to the callback's parameter list and .bind()ing them where you use it; the latter similarly, but you also have to introduce a layer of indirection via an object to get a "pass-by-reference"-like effect. So sometimes it really is simpler to just use a function expression in-place. To contrive a quick motivating example: var odds = 0, evens = 0; array.forEach(function (x) { if (x % 2) ++odds; else ++evens; }); console.log("odd:", odds, "even:", evens); versus function callback (counts, x) { if (x % 2) ++counts.odds; else ++counts.evens; } ... var counts = { odds: 0, evens: 0 }; array.forEach(callback.bind(this, counts)); console.log("odd:", counts.odds, "even:", counts.evens); (And yes, I know you can just use "for...of" for this particular scenario in modern JS; like I said, contrived example.)
- wvenable 10y agoA lot of one-time-use functions aren't necessarily more readable either. You can start to the lose the context of when, where, and why it's being called. Many small functions or nested anonymous functions are just two ways of dealing with a difficult situation.
- nojvek 10y agoPromises solve callback hell. Async await makes it even cleaner. I think the way author is explaining is weird. It's very hard to follow logic when functions are declared all over the place.
- illuminati1911 10y agoMy recommendation for handling callback hells, not just in JS but in any language with callbacks is FRP. http://reactivex.io http://reactivex.io Basically it's observer pattern on steroids. I'm not good at explaining things though so check out the website.