Hacker News new | ask | show | jobs
Avoiding Nested Callbacks in JavaScript (ablogaboutcode.com)
15 points by panthomakos 5468 days ago
5 comments

These aren't very good examples. You shouldn't break up small pieces of code just to avoid nested callbacks.

"Well, until you have two or three callbacks that respond differently to fs.readFile and you start running out of function names, right?"

That's a red flag right there. If you can't figure out a unique name for the callback, perhaps it's not worth naming it.

If you have a reusable piece of code, or it's just getting unwieldy (~3 levels deep or so), then you should consider breaking it out into it's own function. But otherwise for simple things I think it's more readable to have it inline.

It's unfortunate JavaScript's anonymous function syntax is so verbose. It would be much more pleasant with lightweight syntax (CoffeeScript, perhaps?)

It is verbose, but on the other hand, I like the fact that the syntax for anonymous functions is pretty much almost the same as named functions.

Like what pg said somewhere (forgot where), having anonymous functions is a red flag in the language, because there shouldn't be any distinction between anonymous functions and named functions. Javascript kind of gets this right.

I believe CoffeeScript gets this even more right, by having a single way to declare functions which is also concise?

Translating the original example, sans error throws:

    fs = require 'fs'

    fs.mkdir './hello', 0777, ->
      fs.writeFile './hello/world.txt', 'Hello!', ->
        console.log 'File created with contents: '
        fs.readFile './hello/world.txt', 'UTF-8', (err, data) ->
          console.log data
The currently being worked on future version of javascript will have less verbose syntax. Brendan Eich has blogged[1] about his proposals.

Instead of

  (function(x) { return x * x })
you could just write

  #(x) { x * x }.
[1]http://brendaneich.com/2011/01/harmony-of-my-dreams/
I really like the way functions are declared in Kaffeine[1]:

  (x) { x * x }
[1] http://weepy.github.com/kaffeine/docs/function_extensions.ht...
I thought the # shorthand for functions proposal was rejected?
It was indeed. None of the function shorthands made it into ES.next, but the current leading contenders are: http://wiki.ecmascript.org/doku.php?id=strawman:arrow_functi...

    (x) -> x * x
And http://wiki.ecmascript.org/doku.php?id=strawman:block_lambda...

    {|x| x * x}
I think you make good points. The reason I used these simple examples was only for simplicity and to illustrate that named function callbacks can be used within closures rather than littering your global space with function declarations. More complex examples would probably have taken away from the core point of the article.
It's understandable you wanted simple examples, I just worry beginners will take it literally and name every callback.
It's still nesting callbacks, just not inline.

Also, for the second example, this might be a cleaner solution:

    var fs = require('fs');

    function writeFile(filename){
      return function(err){
        if (err) throw err;
        console.log('Wrote ' + filename);
      }
    }

    function writeHello(){
      return fs.writeFile('./hello.txt', 'Hello!', writeFile("hello.txt"));
    }

    function writeWorld(){
      return fs.writeFile('./world.txt', 'World!', writeFile("world.txt"));
    }

    writeHello();
    writeWorld();
Absolutely - the code can be re-factored - but your example doesn't demonstrate that you can used named functions/callbacks within closures to further organize code.
Wouldn't this have a performance issue since the nested function is being redeclared every time the outer function is called?
You mean the nested versions? Just because it's not the most efficient way possible to do something does not automatically mean it's a 'performance issue'. If you're doing it once, or if it's some user based event like a button click, the difference will not be noticeable. If this is happening in a loop, perhaps then you might be in need of re-factoring. But in this example it would probably hardly help since it's reading and writing files, and chances are that's going to slow you down more.

EDIT: took out function hoisting stuff, it doesn't get hoisted after a quick check.

It's certainly no worse than the first example with the anonymous functions inline. But yes, worse than the second example, since it's creating closures on each invocation.
The closures aren't meant to be more performant, they simply allow you to separate your code. It's not fair to compare the unnamed function example with the closure example, since the first doesn't create closures. If it did - which would be necessary to create both functions - the examples would be equally performant.
The first and third examples are both creating closures. It doesn't matter if they're named or not. Any time you see the "function" keyword within another function, that's a closure.

Check out the performance of these three styles. #1 and #3 are almost identical, #2 is significantly faster: http://jsperf.com/closures-perf

You make a good point and thanks for the performance link. I should have been more clear. The first and third examples are only comprable in the way they create closures - which is equivalent, but they are meant to demonstrate different things. The first demonstrates nested callbacks while the last example demonstrates named functions that are organized by closures.
But this is happening anyways with unnamed functions - they will be defined any time the top level function is called. The difference is really just whether or not you are naming your functions.
Why?

"You should donate all your money to praeclarum." "Why?" "Because I said so."

I think the tone of the title is ambiguous. After reading the article, I felt it was more of

"[Here's an example of] Avoiding Nested Callbacks"

instead of

"Avoiding Nested Callbacks [is important and something you should do]"

Please elaborate on your question.
My reading of the title followed the first commenter.

For years we've been begging for locally bound function declarations so we don't have to explicitly pass variables to callbacks and so we don't pollute the global namespace with worker function names.

This article then proceeds to throw all that away so we can have named functions. Worse, you can have names and locality that he ignores:

    function bigGuy() {
        function littleWorker() { /* stuff */ }
        begin(littleWorker);
    }
So my question, why do this? What are the benefits?
Is this not an obvious effect of first class functions? Was it necessary to rename "functions" to "named callbacks"? Can't you do basically exactly this with function pointers in C?

Hopefully v8 will implement some of the things from Mozilla's Javascript 1.8 like generators, which will make evented programming more interesting.