
Callbacks are Pretty Okay - AndyKelley
http://andrewkelley.me/post/js-callback-organization.html
======
gruseom
I am wary of designs that make code significantly longer, as both these
examples do. The improvement in local readability is often superficial and the
cost in overall readability (not to mention bugs, which increase with code
size) is often nontrivial.

In the examples, each line of code with a named function appears more
readable, but this is only because its meaning has become less dense—and as
there is also now more code to read, the two factors arguably cancel each
other out. Meanwhile the new code has more non-locality: the eye of the reader
must jump between the various function invocations and declarations. That's a
lot of jumping, and it will get much worse as complexity grows.

Introducing a name has costs as well as benefits. For example, it adds
indirection: the name now stands between the reader and the thing that is
named. To overcome this, a name should say something about the problem the
code is solving. If all it says is something about the code itself, that's not
good—it adds weight and obscures the problem (because you're now taking more
code to solve it). It feels like you're simplifying code by factoring it in
this way, but you may only be spreading it out more, making pieces which are
logically bound together further apart from each other.

A sign that this is happening is when you can't think of a good name for the
code you are factoring, or when the name refers only to the code itself.
"wtfFriendAction" is a comical example, but I think "gotFriends" is more
instructive: it tells you nothing more than "I am the callback for
getFriends", and thus adds no information to the original code, only
verbosity.

~~~
jay_kyburz
I don't think naming your functions has to make the code "significantly
longer". Perhaps 2 lines per callback.

And yes it can be hard to name functions, and I think the OP has not created
very good example, but a good name that accurately describes what the function
does means I don't have to read it if I don't want to.

A better example would have been some node.js code that has 5 levels of
nesting, each with a some significant code within.

You've just got to name them.

~~~
gruseom
The examples are 40% and 20% longer. That is significant.

The trouble with small examples is that their problems don't seem like a big
deal because their entire codebase isn't a big deal. You have to apply them
many times over to get any sense of their compounding effect, which is what
really matters. But by definition, that's out of scope of a discussion whose
examples are small. The only way out of this paradox is to pay close attention
to apparently small effects.

~~~
mattmanser
One of the things that really annoys me about modern JavaScript programmers is
that they seem to have forgotten the basics.

Functions are good. Short methods are good. Reusability is good. Properly
naming your functions is good. Separation of concerns is good.

Complaining about a couple of extra carriage returns and a few extra
characters to properly name the method is insanity.

~~~
gruseom
How many functions are good? If I add a dozen, is that better? Short methods
are good—have any evidence for that? I don't think you do, because such
evidence as there is points in the opposite direction (look it up in _Code
Complete_ ). It's proper to name functions properly? I agree! What's
"properly"?

Hammering these dogmas harder doesn't make them any truer, it just creates a
feeling that you know what you're doing in a "If the English language was good
enough for Jesus Christ then it's good enough for the state of Texas" kind of
way. We have almost no empirically verified knowledge about software
development. One of the few things we do know (sort of—the studies are old and
shaky) is that code size is a predictor of error rates. Think that's relevant?

~~~
jasonlotito
> How many functions are good?

As many as needed.

> If I add a dozen, is that better?

If your programs path needs to do 12 things, then yes. Each function should do
one thing. If in documenting your function, you use the word _and_ , that's a
safe bet that you are actually writing two functions as one.

> Short methods are good—have any evidence for that? I don't think you do,
> because such evidence as there is points in the opposite direction (look it
> up in Code Complete)

Unless there is another reference I missed, Code Completes reference referred
to no more than a screen's height, which at that time was ~ 25 lines. Assuming
not every line is a line of code performing some action, I'd still consider
that a small function.

> It's proper to name functions properly? I agree! What's "properly"?

That's hard to answer, which is why so much has been written on it. Couple
that with community idiosyncrasies (like Obj-C naming vs Java), you have a lot
of "proper" ways to define functions. Within the realm of the community
standards, I subscribe to the thought that the method names should tend to be
verb oriented. Talk about actions, or perform an action. After all, functions
do something.

> We have almost no empirically verified knowledge about software development.

Well, there is quite a lot. Much of it's older (in the history of software).

> One of the few things we do know (sort of—the studies are old and shaky) is
> that code size is a predictor of error rates. Think that's relevant?

Be careful about this. It's not as open and shut as you'd like to think.
Indeed, it's about as verifiable and confirmed as cyclomatic complexity is.
Without going into too much detail and sticking strictly the context here:
"code size" is an awful metric.

By that notion:

    
    
        int a = b + c; 
    

Is dramatically better than:

    
    
        int hours = timesPerformed + hoursPerPeformance;
    

Layer that into the concept of lines of code: is that lines of functional
code? Lines of any code that isn't a comment? Any lines that aren't a comment?
Lines of code that are only functional? All lines, including comments? All
lines, including comments, except for standard headers?

All of those are valid deductions. After all, while it might seem obvious to
remove comments, they could cause confusion, right?

And this ignores even the basic question: is a line merely a line on the
screen? Or is it a languages statement? A single operation? Would a complex
for clause with multiple assignments and statements count as one line?

All this is to say that code size is not the holy grail of error rates.

My own though is that taking CC and LoC and putting them together is best. A
function with low complexity and with fewer lines of code is better than a
longer function. Many smaller, lower complexity functions are also better than
a single highly-complex function that is longer than any single function, but
smaller than all the others when combined.

~~~
tlrobinson
Number of tokens is a better metric than number of characters.

~~~
akkartik
Yeah I'm sick of that obvious strawman being trotted out by the function-
bloater side everytime we try to talk about how code size is important. I'm
sure there's other examples where small code size isn't necessarily better --
but nobody bothers to look for them!

In practice I find I'm a terrible judge at where the function boundaries
should be. Several times I've tried to reorganize a project where functions
had gotten gradually too big, ended up making them too small and numerous and
having a pain debugging, then finally settling on some intermediate position.
All the overly-certain talk of "as many functions as you need" misses these
nuances.

(I wrote recently about global vs local readability:
[http://akkartik.name/blog/readable-bad](http://akkartik.name/blog/readable-
bad))

~~~
jasonlotito
> Yeah I'm sick of that obvious strawman being trotted out

So, you think it that code should be measure by the size of the code?

a = b + c; is better than the other?

Care to explain why?

~~~
jasonlotito
Either he's wrong about the straw man, or he's asserting that the number of
characters used is an important factor in code quality. I'm searching for
clarification of which he's asserting.

~~~
akkartik
Sorry, I think my response was sacrificing clarity for rantiness.

I meant that everytime somebody refers to the value of controlling code size
(like gruseom did in this thread), we have to deal with the inevitable
strawman of:

    
    
        int a = b + c;
    

vs

    
    
        int hours = timesPerformed + hoursPerPeformance;
    

But gruseom's comment has nothing whatsoever to do with short vs long variable
names. tlrobinson interpreted right that codebase size should be measured in
number of tokens. Measured in tokens, both examples above have identical size.

(PG has also said this many many times with reference to arc.)

------
prezjordan
Right, but now your functions are very tightly coupled to each other. The
indentation might be nicer, but functionally it's just misleading.

The cool thing about promises is that your methods are pretty portable. For
example:

    
    
        function getUserFriends(userName, next) {
            db.users.findOne({name:userName}, foundOne);
    
            function foundOne(err, user) {
                if (err != null) return next(err);
                db.friends.find({userId:user.id}, foundFriends);
            }
    
            function foundFriends(err, friends) {
                if (err != null) return next(err);
                return next(null, friends);
            }
        }
    

Can be rewritten (using q[0]) as:

    
    
        var Q = require('q');
    
        function getUserFriends(userName, next) {
          var deferred = Q.defer();
          db.users.findOne({name:userName}, deferred.makeNodeResolver());
          return deferred.promise;
        }
    
        function foundOne(user) {
          var deferred = Q.defer();
          db.friends.find({userId:user.id}, deferred.makeNodeResolver());
          return deferred.promise;
        }
    
        function foundFriends(friends) { ... }
    
        getUserFriends.then(foundOne).then(foundFriends);
    

You'll see here that getUserFriends, foundOne, and foundFriends (which I left
out) are now all portable and can be used elsewhere in the code for other
purposes (because they are no longer tightly coupled).

[0]: [https://github.com/kriskowal/q](https://github.com/kriskowal/q)

~~~
StavrosK
With the downside that I have _absolutely no clue_ what this code does or how.

~~~
DrJokepu
Once you get used to promises (it took me a couple of days) it's not confusing
at all. It takes a bit of effort though. I remember when I first started
programming none of the programs made any sense to me; as you said, I had
absolutely no clue what the code did and how. But then I kept practicing and
soon I understood how things work.

~~~
StavrosK
Yeah, I guess it's not fair for me to compare something I'm familiar with to
something I'm not. I'm sure promises are easy to understand, but that API
there does not look great.

------
chmike
The real problem with callbacks is error handling. Using anonymous functions
doesn't address this problem. The _.then_ notation has the same problem.

The _await_ solution (or any equivalent) has the benefit to present the call
as a synchronous call which is trivial to read and understand. It also has the
benefit to support all existing and proven error handling methods. try catch,
etc.

This is the best solution to asynchronous calls that I have seen so far.

I'm currently using c++ asio, and it's a nightmare.

~~~
lucian1900
Promises are actually pretty good at error handling, the first error
encountered is returned.

~~~
Nitramp
Well, Q allows you to attach an error handler with catch:

    
    
        bla().
            then(something).
            then(someOther).
            catch(ohNoes);
    

First, this gives you really bad stack traces, essentially you have no chance
to figure out what happened before your error handler got called. You can get
slightly less bizarre stack traces with `Q.longStackSupport = true;`, but that
has a serious performance impact (creating a new throwable per invocation).

Second, if you try to get better code structure by breaking up your program
into named functions ('something', 'someOther' above), you loose locality,
i.e. your error handler does not share a scope with its causing code. You can
fix that by passing the entire promise to a processing function, but this
again obscures things.

Compared to a program that just does:

    
    
        x := bla();
        var y;
        try {
          y = something(x);
        } catch(...) { ... }
        return someOther(y);
    

The promise code is still a lot more complicated. I agree with Miguel de
Icaza, continuation passing style/callbacks do destroy 'natural' program
structure, and make it much harder to use the regular programming language's
idioms (loops, returns, ...).

I'd be hugely in favour of go's approach with very cheap goroutines, so you
don't actually have to use CPS just because your runtime isn't clever enough
to use more than one thread ( _ahem_ node). OTOH go's lack of exceptions kills
the whole argument by bringing back the error handling of the 80s :-(

------
jameshart
How can you make this argument and not even mention the word 'closure'? That's
like arguing there's no need for object orientation, and demonstrating it by
showing how you can model any class as just a set of global functions, by only
showing classes which have no instance state.

Replacing anonymous functions with named functions works fine unless the
anonymous functions are closures. If they are, you're going to have to add
parameters to them to pass state in, and you're in real trouble if you want to
have them modify closed state, because in JavaScript you've got no pass-by-
reference. But closures that modify closed variables are hard to reason about
in the usual nested callback syntax model.

What the async 'syntactic sugar' approaches do is replace nested closure
scopes with simple control-structure scoping that's easy to reason about -
even if the 'callbacks' are modifying state which then gets made available to
subsequent callbacks.

~~~
zemo
It looks like you opened the article, hit "ctrl+f closure", saw 0, and went on
a rant. In the examples where the turned an anonymous function into a named
function, the named functions still close over symbols from their calling
context, so... it's hard to see what you're on about.

~~~
jameshart
His examples only close over parameter variables and lifted functions, not
local variables that actually, you know, vary.

The fact is, in JavaScript, lifted functions can close over local variables
but the fact that they do is not obvious - in fact it's so counterintuitive
that you're generally best to avoid doing so.

In the first example he gives, two functions that, in the original, are
nested, in the refactoring are placed in the same scope, rather than nested
within one another. His amended solution prevents the second function from
closing over the scope of the first, which is possible in the original, or an
async form. If you wanted to share state from the first function to the
second, you'd have to realize you _could_ bring the second function into the
scope of the first, and then move it, and then you're back where you started
with nested callbacks. But amending this code is made harder than it should be
by the decision to factor the two closures out to the widest possible scope,
rather than the narrowest.

And the fact that ctrl-F closure yields no results just confirms the feeling
that the examples gave me that the author is playing around with closures
without knowing what he's actually doing.

------
sklivvz1971
I think callbacks are still pretty broken even after applying the "two rules".
Yes, one avoids nesting, but you still break your procedure/function/method
into chunks _which make no sense by themselves_.

I don't see how that can lead to good code. Better than a million nested
callbacks? Sure. Readable? Nope. How do you even name all those callbacks?

------
coldtea
"Callbacks are Pretty Okay" translation: I'm a programmer in my twenties, work
mostly in Javascript, have no much experience with the historic literature and
core ideas of Compute Science, don't know any better, and I like node, so
there.

~~~
nly
[http://andrewkelley.me/post/not-a-js-
developer.html](http://andrewkelley.me/post/not-a-js-developer.html)

------
hosay123
This recent 'callbacks as goto' discussion is so utterly mundane that I've all
but failed to convince my wrists to so much as twitch sufficiently to click on
any of the links, just the title is enough to drive me to drink.

Callbacks are _in no way_ "this generation's goto", they do not _in any way_
inhibit the ability to analyse or prove correct a program, and all
alternatives to callbacks amount to _incredibly fancy syntax sugar_ for hiding
what is really happening behind the scenes anyway.

A callback is a function call, or put another way, a specification of an exact
set of arguments grouped in a formal specification that is sent to a
deterministic little machine (the function) to execute immediately. On
completion the machine will provably produce the result it promised to return
in its owner's manual (i.e. the declared return code).

None of this is "goto" in any way. In goto land, without knowing the exact set
of inputs a program receives and whole-system simulation, it is utterly
impossible to make assumptions about even the most minimal pieces of state in
the program at any given moment.

Contrast to a function call. A function call is a fixed guarantee about the
state of the system at the point a machine begins executing. Furthermore a
function call has a vastly restricted set of valid behaviours: at some point
it must terminate, and prior to termination, update the system state (stack)
to include its return value. And upon termination, result in the calling
machine to resume operation.

All this syntax sugar shit is _whack_. Great, you typed 24 characters instead
of 3 lines to declare a lambda. Hyper productive, go you. How does this
progress toward the point where I can say "computer, make me a coffee!"?

If you're genuinely interested in what this generation's Goto might look like,
take 30 minutes to watch
[http://vimeo.com/71278954](http://vimeo.com/71278954) .. our UIs are utterly
trapped in pre-1970s thinking, our communication networks are so utterly
idiotic that we _STILL_ have to write custom code to link disparate chunks of
data and logic together, we're _STILL_ writing goddamned function calls in a
freaking text editor (something that was solved LONG ago). All the things like
this.

I can't ask my computer to make me a cup of coffee, and it responds. I can't
describe in restricted English a simple query problem and have the millions of
idle machines around the world coordinate meaningfully to answer my question
(and our best chances of this.. freebase.com.. got bought up by Google and
left to rot as some vestigial appendage of their antispam dept no doubt).

Computing is in a sad state today, but not because of fucking callbacks. It's
in a sad state today because _we 're still thinking about problems on this
level at all_.

Node.js wasn't an innovation. _It was shit that was solved 60 years ago_. I
wish more people would understand this. _Nothing improves if we all start
typing 'await' instead of defining callbacks_.

Innovation in our industry has been glacial since at least the early 90s.

Edit: and as a final note, I wholeheartedly welcome you to nitpick the hell
out my rant, wax verbose on the definitions of provability, the concept of a
stack, the use of English versus American language, why Vim is awesome, and in
the process once more demonstrate the amoeba-like mentality 90% of our
industry is trapped in.

Go and build a programming language your Mom can use. Preferably by talking to
her computer. Please, just don't waste it on any more of this insanity.

~~~
coldtea
> _Callbacks are in no way "this generation's goto"_

They are very much analogous to GOTO -- or even COMEFROM.

They have a similar effect to the control flow, and a similar adverse effect
on the understanding of the program.

And Dijkstra's core argument against GOTO applies 100%:

"""My second remark is that our intellectual powers are rather geared to
master static relations and that our powers to visualize processes evolving in
time are relatively poorly developed. For that reason we should do (as wise
programmers aware of our limitations) our utmost to shorten the conceptual gap
between the static program and the dynamic process, to make the correspondence
between the program (spread out in text space) and the process (spread out in
time) as trivial as possible."""

> _all alternatives to callbacks amount to incredibly fancy syntax sugar for
> hiding what is really happening behind the scenes anyway._

If, for, while, foreach --heck even map, reduce et co, etc are also syntax
sugar for GOTO. Your point? Or adding the weasel word "fancy" somehow makes
this particular syntax sugar bad?

Not to mention that there's nothing "incredibly fancy" about await, async,
promises et co.

And if I wanted to know "what's really happening behind the scenes" I wouldn't
programmer in Javascript in the first place.

~~~
gruseom
I thought of that Dijsktra quote too. It sheds light on what's wrong with the
OP's proposal: in return for less nesting, it worsens the conceptual gap
between the program (in text) and the process (in time). A poor trade.

I've found that nesting to indicate asynchronous control flow can be quite a
good device for keeping the program text closely related to its dynamic
execution. (It's true that such code is hard to read in JavaScript, but I
don't think that's because of nesting per se.) It allows you to lay out
synchronous logic along one dimension (vertically) and asynchronous along
another (horizontally). I hypothesize that there's a quite good notation
waiting to be brought out there; unfortunately, such experimentation tends to
be done only by language designers in the domain of language design, when it
really ought to be done in the context of working systems—and that's too hard
to be worth the trouble unless you're programming in something like a Lisp.

~~~
coldtea
> _I thought of that Dijsktra quote too. It sheds light on what 's wrong with
> the OP's proposal: in return for less nesting, it worsens the conceptual gap
> between the program (in text) and the process (in time). A poor trade._

That's like saying that "by using FOR instead of GOTO we worsen the conceptual
gap between the program (in text) and the process (in time)".

Only we don't worsen it -- we just abstract it to a level in which we can
reason about it better.

Higher level is not worse -- except if you consider "more distance from what
is actually happening at the CPU" as worse. Which is not: historically the
higher the distance, the more programmers got done.

We don't need to keep track of the actual low level process in time -- which
with callbacks we're forced to. We just need to know how the steps of the
process relate. Which await and co offer in a far better form than callbacks.

~~~
gruseom
We're not talking about distance from the CPU. At least I'm not, and I'd be
shocked if Dijkstra were. No, the issue is that a program has at least two
different logical structures: its lexical organization in text, and its
dynamic organization in time. You want these two to be as closely related as
possible, because humans rely mostly on the program text to understand it.

"Time" here doesn't mean CPU time, it means logical time--the temporal (as
distinct from textual) order of operations in the program. To put it another
way, the "time" that matters for program clarity is not when stuff happens at
lower levels (e.g. when does the OS evict my thread) but what happens when in
the source code itself. This is not so far from your phrase, "how the steps in
the process relate", so I don't see the disagreement.

I certainly don't agree, though, that the OP's design recommendations lead to
higher-level or easier-to-reason-about code. Do you really think they do?

~~~
aufreak3
> ... the issue is that a program has at least two different logical
> structures: its lexical organization in text, and its dynamic organization
> in time.

That's a concise way to put it and I'll certainly remember and reuse it! Did
you come up with it or did you come across it somewhere?

~~~
gruseom
It's just a paraphrase of the Dijsktra quote. The same idea is implicit in the
distinction between lexical and dynamic scope, which confused me for years
before I got it... probably because "dynamic scope" is something of an
oxymoron.

------
rob05c
I have a project in Go which is pretty heavily functional. Over time, I've
begun to notice the general goodness of his 2 rules. If you didn't RTFA, they
were

1\. Avoid nontrivial anonymous functions.

2\. Function declarations after the code that actually does things.

I wonder if his advice is good for all functional-style code in non-functional
languages.

------
coldtea
> _In this refactored code, we 've only reduced the maximum nesting by 1 -
> from 8 to 7. But consider how much easier it is to follow._

Are you fucking kidding me?

------
a__remie
Nesting is not the problem, you see what you're doing (when you perform an
async io request). And you still have the "if (e) return cb(e)" line... And on
top of that, it does not look better.

------
casperc
I guess it comes down to preferences in readability.

Personally I agree that not putting too much logic in anonymous functions
makes it more readable. I write my own code that way, but some might disagree.

I don't care about whether functions are above or below where they are used,
as long as it is consistent - but event then it is a minor thing. And since it
isn't enforced by the language (e.g. like in Clojure where you have to def the
var/function above the reference) it is going to vary across code bases
anyway.

I don't really think either solves callbacks, nor that callbacks is a problem
to be solved. Most comes down to pros and cons seen through the filter of your
personal preferences.

Sure you can use a language that compiles to javascript. Does the pros
outweigh the cons for you? Great, use it/too bad move right along.

Not everything is right or wrong, good or bad - in fact most things aren't.

~~~
ulisesrmzroche
Thing is that it actually matters in JS because declaring a function this way
triggers the hoisting mechanism, so named functions are in scope within the
entire function in which it is declared, regardless of block nesting.

If one isn't careful, and because JS doesn't have the visual delineator of
blocks, instead all these rules you have to memorize, this could easily could
lead to bugs, especially in large code-bases.

I think it's good to rely on closures to keep from polluting your scopes and
to not use uneccesary function names, which is going to lead to one giant
imperative statement (the one with the sequence of commands for the computer
to perform) rather than a lot of little functions that you can pass around.
Secrets of the JS ninja goes really in depth about this.

~~~
esailija
There is no such thing as an actual hoisting mechanism. Yes, declared
functions are available anywhere in the surrounding scope, _just like in any
other language_. It would be ridiculous if for example you couldn't call a
method in Java inside another method unless it was syntactically placed before
the target.

Global scope pollution is completely irrelevant here - declarations are
available in the surrounding scope, not global. If the surrounding scope is
global (which it never is in node.js), then _that 's_ your problem.

~~~
ulisesrmzroche
I don't think that's true about the hoisting mechanism. The javascript
interpreter looks ahead to find all the var and functions and hoists them to
the top of the function. I don't how know Java deals with forward-references,
it was already considered passe before I even got into this stuff.

Also, where did I write anything about global scope? I said it was good to
rely on closures to keep from polluting your scopes, and that there was no
need for with unnecessary function names. You're literally belittling me out
on things I didn't even write. Am I upvote-target practice here or something?

------
aa0
An operation queue where dependencies can be set is also very relevant. For
example, if you have to do 2 different json requests, followed by a render -
but they are mutually exclusive, you can do a 2->1 dependency. If they do
depend on each other's result, you can do a 1->1->1

I like this async syntax but I'm more fond of native JS generator trampoline.
I think the JSOperation with dependencies could even use generators in its
implementation.

All these solutions coming about after the C# async article are very
enlightening to this problem of the synchronic vs diachronic vs asynchronic.

The JSOperations lib is in alpha but can be found here
[https://github.com/iandrewfuchs/JSOperations/blob/master/REA...](https://github.com/iandrewfuchs/JSOperations/blob/master/README.md)

------
rickygw
Thanks for this OP. I use these 2 rules in my every day JS because where I
work, readability wins every time. The anon function / indentation code format
is something I've never really tried to "pick up" when I migrated over to
javascript. Its ugly and takes longer for someone outside of my head to figure
out what is going on in my code. Verbose function names help the other 4 guys
on my team know exactly which function is handling the different scenarios of
an async call. Its much more important that the developers are able to work
efficiently than it is to save a few characters per function or a few lines
per callback.

With the state of JS compilers/minifiers and gzip, I really feel that that
particular argument can't even be made in most JS situations.

------
tkiley
I like Andrew's rules here, and reading over one of my recent node.js
projects, I can see how they would have helped.

My code becomes cleaner and more maintainable when I am forced to name each
chunk of 5-10 lines by putting them into separate functions/methods with
defined inputs and outputs. This compartmentalization is naturally encouraged
by java, C#, ruby, and python, but Javascript actually seems to discourage it.

The ready availability of anonymous callbacks in JS tends to allow for
"function creep", where you blink twice and suddenly you have 50 tangled lines
of code without obvious control flow or separation of concerns; sticking to
named functions seems like it could definitely help.

------
nickjackson
I agree with the OP. Its all about how you structure and name your code to
avoid deeply nested callbacks. With the use of Prototype constructs and decent
directory structures you can keep code clean and concise and completely avoid
callback hell.

Take a look at the following users repos to see exactly how clean javascript
code can be.

[https://github.com/learnboost](https://github.com/learnboost)
[https://github.com/component](https://github.com/component)
[https://github.com/visionmedia](https://github.com/visionmedia)

------
chapel
This is essentially how I have been writing JavaScript and Node.js code for
some time. I actually flatten the code even more, like in these examples[0].

For some people, callbacks take more cognition than other solutions, but for
someone who has been using callbacks through and through, this is a natural
progression and makes sense. It is more straightforward and you don't have to
understand further abstractions like in a compile to JS language or with
promises.

[0]:
[https://gist.github.com/chapel/6255720](https://gist.github.com/chapel/6255720)

~~~
antihero
I think I've done pretty much exactly the same thing:

[https://gist.github.com/radiosilence/2fd82aaf3b1721448ae1](https://gist.github.com/radiosilence/2fd82aaf3b1721448ae1)

Except your function names make more sense.

------
wingspan
I never thought of using nested functions like that. It does make the code
much more readable, and using 100% JS is a big win. Not as pretty as
yield/await, but much more debuggable and compatible.

------
ulisesrmzroche
I think this is a step backward toward procedural programming (needing to name
each function, ignoring the hoisting mechanism that happens when you declare a
function that way) and then took another step back by putting it all in one
huge imperative statement at the top). Probably going to lead to bugs if one
isn't careful with the function scope. The best part about JS is that you can
be use a functional, declarative style, which is awesome.

------
AYBABTME
The main problem I see with callbacks is that people forget that deeply nested
structures are too cumbersome to hold in one's brains. The OP rules to break
down things in smaller, named function is a solution to a more general
problem: don't nest things if you can avoid to.

Callbacks aren't the problem. People using them are the problem. Keep your
logic as flat as possible and callback-hell will dissolves before your eyes.

Programming dogma is so annoying.

------
antihero
"Coffee-Script actually prohibits this kind of code organization, because all
functions are necessarily assignments"

How about this then? I haven't tested. Perhaps functions need to be in a
different order, but we only have one level of nesting:

[https://gist.github.com/radiosilence/2fd82aaf3b1721448ae1](https://gist.github.com/radiosilence/2fd82aaf3b1721448ae1)

------
zhyder
You don't need any nesting. All those methods could be declared at the same
level, though that entails passing more params to them since you can't rely on
closures. That's probably how you'd divide your synchronous code into methods
anyway, so you might as well do the same with your async code.

------
undoware
This dude fails the understanding-LiveScript test. Next time you bash
something, read up about it, okay? 'backcalls' are purely syntactical --
they're callbacks, but with a special syntax so you don't have to indent.

~~~
AndyKelley
That's what I thought, but someone claimed otherwise:
[https://news.ycombinator.com/item?id=6227958](https://news.ycombinator.com/item?id=6227958)

Instead of investigating and finding out for sure, I communicated my
incomplete knowledge without context. You're right - I should have
investigated and then communicated the correct knowledge. Any LiveScript
bashing was unintentional.

------
eonil
This seems misled response to misled article.

------
pacala
callbacks are also known as continuations which are known as generalized goto.

------
Gonzih
No, they are not.

