"Await" is fantastic, and having using it for JavaScript (via TameJS and then IcedCoffeeScript), it makes things a lot easier and clearer.<p>That being said, I don't think the comparison between callbacks and goto is valid.<p>"Goto" allows you to create horrible spaghetti-code programs, and getting rid of it forces you to structure your programs better.<p>"Await", fundamentally, isn't really anything more than syntactic sugar (except for exception handling, which is a good thing). "Await" doesn't change how your program is structured at all, it just changes the <i>visual</i> representation of your code -- from indentations in a non-linear order, to vertical and linear order. It's definitely a nice improvement, and makes code easier to understand (and allows for better exception handling), but it's not actually changing the way your program is fundamentally structured.<p>And finally, "await" is only applicable when a single callback gets called once at the end. If you're passing a callback that gets used repeatedly (a sorting function, for example), then normal-style callbacks are still necessary, and not harmful at all. Sometimes they can be short lambdas, sometimes they're necessarily much larger.<p>In sum: "await" is great, but there's nothing inherently harmful about callbacks, the way "goto" is. To the contrary -- callbacks are amazingly useful, and amazingly powerful in languages like JavaScript. "Await" just makes them nicer.
Yes a thousand million times. This is the reason why people love golang and why there's a lot of excitement about core.async in the Clojure community, particularly for ClojureScript where we can target the last 11 years of client web browser sans callback hell:<p><a href="http://swannodette.github.io/2013/07/12/communicating-sequential-processes/" rel="nofollow">http://swannodette.github.io/2013/07/12/communicating-sequen...</a><p>Having spent some time with ClojureScript core.async I believe the CSP model actually has a leg up on task based C# F# style async/await. We can do both Rx style event stream processing and async task coordination under the same conceptual framework.
You get a similar interface in Python's Twisted using the @inlineCallbacks decorator:<p><pre><code> @inlineCallbacks
def example():
try:
obtain_some_lock()
ui_status("Fetching file...")
result = yield fetch_file_from_server(args)
ui_status("Uploading file...")
yield post_file_to_other_server(result)
ui_status("Done.")
except SomeError as e:
ui_status("Error: %s" % e.msg)
finally:
release_some_lock()
</code></pre>
I must say that this style of writing async code is much friendlier than descending into callback hell.<p>There is work to make a similar async interface native in Python 3, in PEP 3156 <a href="http://www.python.org/dev/peps/pep-3156/" rel="nofollow">http://www.python.org/dev/peps/pep-3156/</a>, so this should become more widely available even to those who don't use Twisted.
I'm not buying the c# async/await kool-aid.<p>Async, sure, I'm down with that, but I've used the c# async stuff now, and while it makes it the app somewhat faster, it has three major downsides (that I encountered):<p>- Infects everything; suddenly your whole application has to be async.<p>- Debugging becomes a massive headache, because you end up in weird situations where the request has completed before some async operation completes, the debugger gets scared and stops working.<p>- It's really hard to test properly.<p>The only good reason for using it is that because of the infection-property back fitting async to your application is a major headache; if you might use it, you have to use it from the beginning or you get a huge backlog of refactoring and test fixes to do.<p>-___- 'I might use this for some performance bottleneck I don't yet know about, so better start using it now...' yeah, that's a thing: premature optimization.
As someone who only recently switched to Node.js from PHP I personally haven't had any difficulty switching over to the callback frame of mind, and I haven't experienced the "callback hell" so many people complain about. At first I was hesitant to start with Node because I saw blog posts by people bemoaning the spaghetti callback code that they ended up with. But I haven't experienced any of that, although I am a relatively newbie Node programmer with only a few months of experience so far. My current project is quite non trivial as well, running into the tens of thousands of lines so far.<p>The key I've discovered to nicely organizing callbacks is to avoid anonymous callback functions unless absolutely necessary for a particular scope, or unless the function is going to be so trivial and short that it can be read in a single glance. By passing all longer, non trivial callback functions in by name you can break a task up into clear functional components, and then have all the asynchronous flow magic happen in one concise place where it is easy to determine the flow by looking at the async structure and the function names for each callback function.<p>Another major advantage to a code organization like this is that once you have your code such that each step has it's own discrete function instead of being some inception style anonymous function tucked away inside another function inside another callback it allows you to properly unit test individual functional steps to ensure that not only is your code working and bug free at the top level functions, but also that each of the individual asynchronous steps that may make up some of your more complicated logic are working properly.<p>Most of the bad examples of callback hell that I see have anonymous callback functions inside anonymous callback functions, often many levels deep. Of course that is going to be a nightmare to maintain and debug. Callbacks are not the problem though. Badly organized and written code is the problem. Callbacks allow you to write nightmarish code, but they also allow you to write some really beautiful and maintainable code if you use them properly.
Anonymous Callbacks != Callbacks<p>Callbacks have been around forever in C using named functions, and are not specific to either the current generation of programming languages or programmers. One can still use a <i>named function</i> instead of a locally constructed lambda to represent a callback in a high level languages.<p>The primary difference is that when declaring named functions non-locally, one must explicitly share state through the parameter rather than implicitly sharing state through lexical scoping. It seems more accurate to label the problem of nesting lambdas to the point of ambiguity as "Lambda Abuse" or "Lexical Scoping Abuse" rather than "Callback Hell".
The problem is simply that those languages are not Lisp.<p>Once good patterns of use of GOTO were found, it was natural to critisize random uses, and to wrap good uses in a lisp macro. Or in a new while or for "instruction".<p>But then the next construct is discovered, and its bad uses considered harmful, and its good uses need to be wrapped. In lisp, mere programmers will just write the next macro to abstract away this new construct. Other programming languages need to evolve or have new language invented with new "instructions".<p>So now it's the callbacks. Yes, in lisp we'd just use closures, but this is only a building block for higher level constructs. If those "callbacks" are needed to represent futures, then we'd implement those futures, as a mere lisp macro.<p>Yes, in the other languages you're still powerless, and need to wait for the language designers to feel the pressure and implement a new "future" instruction or whatever.<p>Any language construct can be considered harmful eventually. Concretely, a repeative use is a hint of that: the construct becomes meaningless because it's used all the time, or apparently randomly (just like those GOTOs). But it's not that the construct is bad, it's that its usage is not abstracted away in higher level constructs. And the only way to do that is lisp macros.<p>So unless you're programming in lisp (a homoiconic programming language that let you write easily macros in the language itself), you will always reach a point where some construct will be able to be considered harmful for lack of a way to abstract its uses away.
There is some creative use of C# async/await in this blogpost:<p><a href="http://praeclarum.org/post/45277337108/await-in-the-land-of-ios-scripting-users" rel="nofollow">http://praeclarum.org/post/45277337108/await-in-the-land-of-...</a><p>Basically, the author implements a “first time walkthrough” kind of interface a-la iWork very declaratively by using async:<p><pre><code> async Task ShowTheUserHowToSearch ()
{
await Tutorial.EnterText (searchField, minLength: 3);
await Tutorial.Tap (searchButton);
await Tutorial.Congratulate ("Now you know how to search.");
}</code></pre>
I agree, although I think callbacks are more like COME FROM than goto. You see a function being passed somewhere as a callback, and you know the block is going to execute at some point, but most of the time you have no idea what the codepath that calls you back looks like.<p>There's nothing more frustrating than trying to debug why a callback isn't being called. Who calls it? How do I set a breakpoint somewhere to see why it isn't being called? etc.<p>The one thing that is still missing from await and other green thread approaches is cheap global contexts. Isolating every different green thread so they can't implicitly share state is the obvious next step.
I generally agree that there are better ways to handle asynchronous control flow than callbacks, but I think this is exaggerated. As in most posts like this, the callback soup examples are difficult to follow primarily because they are horribly written, not because of callbacks.<p>As long as you write decent code, the main impediment to asynchronous programming is reasoning asynchronously, not syntax. If you require complex asynchronous logic and don't use an appropriate algorithm, you'll end up in the muck whether you use callbacks or await.<p>Taking go as an example: while I agree that the go statement is more elegant than a callback approach, I see it as quite a minor win compared to channels. The go statement is convenient syntax, but channels are what make concurrency in go feel so robust, and it's a pattern than can be applied just as well in a language that uses callbacks.
I don't understand what the big deal is. Callbacks are OK. They're less cumbersome if the language you're using has smaller function definitions.<p>Callbacks 'get crazy' when you've got more than one I think, and thankfully someone smart has made a library you can use to manage them!<p><a href="https://github.com/caolan/async" rel="nofollow">https://github.com/caolan/async</a><p>Saying that, I don't mind the way things look with the whole await/async stuff in C# and etc. However I don't think we should be waving our arms around saying callbacks are like goto, they so completely are not! I have written heaps of stuff with callbacks and it's _not that confusing or unmaintainable_. It's just different.
Holy Baader-Meinhof, just today, in frustration, I wrote something like Haskell's sequence_ for ContT, in Javascript:<p><a href="https://gist.github.com/cscheid/6241817" rel="nofollow">https://gist.github.com/cscheid/6241817</a>
(C/OS developer spiel)<p>I'm sick of these app developers assuming that using "goto" is bad practice. The fact is that "goto" is used plenty in great production code you're probably running right now.[1] I'd like to know a cleaner way to abort a function into its cleanup phase when a function call returns an error. And "goto" statements are extremely simple to handle for even the most naive of compilers.<p>[1] <a href="https://www.kernel.org/doc/Documentation/CodingStyle" rel="nofollow">https://www.kernel.org/doc/Documentation/CodingStyle</a> (see chapter 7)
Evan Czaplicki (author of Elm lang) made the identical argument (sometime?/years ago), with the same reference to Dijkstra's quote, but with another suggested solution, Functional Reactive Programming, on which his language is oriented:<p><a href="http://elm-lang.org/learn/Escape-from-Callback-Hell.elm" rel="nofollow">http://elm-lang.org/learn/Escape-from-Callback-Hell.elm</a>
Anonymous callbacks are very powerful and very important. They will make you feel bad for unnecessary nesting. They will force you to learn how to abstract better, especially state changes. They will show you how nice and reliable code can be if it doesn't have shared states across multiple functions and how easy it is to understand consistent code with explicit continuations and how to write one yourself. They will make you a better programmer.<p>And "await" can only make it harder to visually distinguish which piece of code is executed in parallel and which is executed sequentially. Nesting makes it explicit.
Why do people insist on analysing things using analogies? Analogies are useful for explaining a concept that might not be obvious. Saying callbacks are like gotos, gotos are bad, therefore callbacks are bad is ridiculous.<p>And he gives some sample code where the 'problem' is nothing to do with callbacks, its just nested lambdas. In fact I find that code quite easy to read, and would be very interested in seeing the same functionality implemented some other way, bearing in mind it is quite a difficult problem to synchronize multiple async operations and usually requires horrible code using multiple mutex.
This definitely is a problem in Obj-C. Using GCD and callbacks is usually easier to understand than delegates and manual thread management, but it's still not great. I would love to see something like async/await in Obj-C. There are some great ideas on how to get something similar in this blogpost, but none that I would use in production code unfortunately:
<a href="http://overooped.com/post/41803252527/methods-of-concurrency" rel="nofollow">http://overooped.com/post/41803252527/methods-of-concurrency</a>
It's right that callback model sucks, and the <i>task</i> model is a way to go.<p><pre><code> Sadly, many developers when they hear the word "C# async"
...
All of these statements are made by people that have yet
to study C# async or to grasp what it does.
</code></pre>
But it's unpleasant to see the author is talking concept of task - lightweight threading, coroutine, or whatever - is like a patent of C# (or F#). And furthermore, treating <i>many developers</i> are not able to understand this concept.<p>Maybe true for the people around him.<p>I understand his position as a lead developer and an evangelist of Mono/C#, but this attitude is ridiculous.
ES6 generators combined with promises will bring this to the javascripters: <a href="http://taskjs.org/" rel="nofollow">http://taskjs.org/</a>
In the right places and for the right reasons they are fine. A lot of code today devolves into what I've come to call "callback spaghetti" and, well, good luck. The toughest thing sometimes is getting your mind around what is supposed to happen and, more importantly, what is not.<p>I found that sometimes it helps to build a state machine to effectively run the show and try to limit callbacks to setting flags and/or navigating the state tree. State machines make following code functionality a breeze, even when dealing with really complex logic.
Callbacks are basically COME FROM, epecially on a platform like a cell phone where you at least in theory have limited processing resources ($40 android phones need apps, too!). They are the devil.
iced-coffee-script has a similar solution. <a href="http://maxtaco.github.io/coffee-script/" rel="nofollow">http://maxtaco.github.io/coffee-script/</a>
I was expecting to read something about FRP or other naturally reactive programming models that dealt with the semantic complexity of callbacks, not just their syntactic complexity. I don't think async constructs and others that depend on CPS techniques are really going to save us from complex programs that we barely understand.
Meh.<p>Callbacks are a very limited way to do asynchronous programming. However they are a good way to create interfaces that let you call methods and insert your own functionality in the middle.<p>So yes. Better async is good. But don't take away callbacks. They have their uses.
lthread is a coroutine library that allows you to make blocking calls inside coroutines by surrounding the blocking code with lthread_compute_begin() and lthread_compute_end(). This is equivalent to async calls but without the need to capture variables.<p><a href="http://github.com/halayli/lthread/" rel="nofollow">http://github.com/halayli/lthread/</a><p>Disclaimer: lthread author
Instead of using callbacks, golang embraces synchronous-style calls and makes them asynchronous by switching between goroutines (lightweight threads). gevent (for Python) does something similar. It's certainly an interesting approach IMO.
I noticed that most of the methods awaited on had an Async suffix in their name. Is that some sort of modern hungarian notation, and is it even necessary? It also looks like you can't pass timeouts to await.
This is all simply sugar to hide behind-the-scenes threading behind very narrow interfaces. Which isn't necessarily bad, but it's fun to see it suddenly in favour again and presented as something new.<p>E.g. Simula67 had Call and Detach for the basic case, and Activate(object representing async behaviour) and Wait(queue) that would both depending on need often be used for the same purpose (as well as a number of other operations). We had to write code using those methods in Simula67 in my introduction to programming class first semester at university...
You can write any article like this about anything, here is the formula:<p>- pick a language feature<p>- write an article with the title "<language feature> as our Generation's Goto Statement"<p>- write an example where you misuse <language feature> and over generalize it<p>- show a workaround that doesn't really save the trouble of actually thinking before typing<p>The hard thing about callbacks is that you need to think about asynchronous processes which can be hard, the callbacks are not the problem so replacing them with something else won't help you too much.
I am very happy that my current (PhD) code doesn't require me to deal with blocking calls to things (it's just one massive calculation essentially). I remember this nastiness from when I had a real job, and I'll no doubt have to deal with it again when I escape academia.<p>This article is very interesting - I enjoy articles which spell out the usefulness of a new language feature. I haven't used C# for a few years, and this is a great advert for coming back to it one day.
I have a basic technical question. I work in C for embedded systems, so I'm a bit "behind the times."<p>How is "await" any different from a regular blocking system call? A regular system call does exactly what is being described: The system call happens, and then when it is finished, execution resumes where it left off.<p>(Yes, this makes the thread block... which is why you have multiple threads. I think the answer will have something to do with this, though...)
Completely OT, but when Dijkstra says:<p>>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.<p>He's touching on a crucial point in Immanuel Kant's philosophy. Kant theorized that though humans received their sensations as a constant stream of input in time (which is an internal condition of human beings, not a feature of bare reality), we can't actually do anything with that stream without applying concepts so as to form concrete (or abstract) objects, i.e. chairs, black holes, mothers, etc. But how would our minds know when to apply this concept or the other? Kant's reply was that our minds look for little clues called 'schematisms' which tell us what the most appropriate fundamental concept to apply to a part of the stream is, upon which others could be combined to produce objective representations we can think and act upon.<p>Almost a hundred years later, Nietzsche will claim (paraphrasing) that a measure of strength in a human being is the extent to which to which they can 'consume' phenomena in time, weakness being how direly one needs to apply a static idea to phenomena (like morals, stereotypes, prejudices, cause and effect, etc).<p>I'm just noting an interesting entry point into an old philosophical conversation. If it's understandable then I hope someone finds it interesting.
The only thing I’ve encountered in modern day programming which is really as evil as goto is aspect oriented programming (AOP). Maybe there are different implementations of AOP but in the one I’ve used you were basically able to hook into every method from everywhere and it was impossible to have any grasp on the flow of the program. That is besides using a debugger.
ES6 generators will be the solution for callback hell in node.js. Node 0.11 already has generators support hidden behind a flag (--harmony-generators) and eventually it will be enabled by default. Generators + libraries like this<p><a href="https://github.com/jmar777/suspend" rel="nofollow">https://github.com/jmar777/suspend</a><p>will make node.js code more readable.
Callbacks are a tool in my toolbox. General event based programming is a tool in my toolbox. Various threading models are a tool in my toolbox. Just because there are situations where a tool is not the best choice does not make the tool bad... it means you use a different tool in that case.
Akka adds something similar in Scala land (and Java) called Dataflow concurrency.<p><a href="http://doc.akka.io/docs/akka/snapshot/scala/dataflow.html" rel="nofollow">http://doc.akka.io/docs/akka/snapshot/scala/dataflow.html</a>
To appreciate whether callbacks are Goto and what to do about them, it is probably good to read a good perspective on Goto from back in the day: <a href="http://cs.sjsu.edu/~mak/CS185C/KnuthStructuredProgrammingGoTo.pdf" rel="nofollow">http://cs.sjsu.edu/~mak/CS185C/KnuthStructuredProgrammingGoT...</a><p>When skimming it, I noticed the appeal to events and the precursors to literate programming (Knuth eventually came up with literate programming a few years after this paper was written).
As soon as you have anything involved that's async to your process or thread, you're going to operate most efficiently with something along the lines of a callback. I don't see them as a Goto at all; they're much more like interrupt handlers or at least event handlers if you want jump ahead a generation from there.
I actually believe that code should be synchronous unless instructed to operate asynchronously - just my .02<p>Await should not be required - it should be more like...<p>--<p>regularWork(); //im waiting till this thing is done<p>driveHome();// not executed till thing one is done<p>background orderStatus = orderPizza();<p>turnOnXbox();<p>while(orderStatus == 'not ready') {<p>playXbox();<p>}<p>turnOffXbox();<p>eat();<p>--<p>Like I said - just my humble opinion that the code written would become more expressive.
Agreed, that's why we're using fibers and common-node (<a href="https://github.com/olegp/common-node" rel="nofollow">https://github.com/olegp/common-node</a>) at <a href="https://starthq.com" rel="nofollow">https://starthq.com</a>
I always liked event based systems the most. I find them to be clean and flexible. Sometimes you want to run some more code after you run an async operation, or you want to run multiple operations at once and deal with them out of order. Await seems pretty linear.
Does Await convert those async calls back into synchronous calls, or what does it do? Because that would be kind of defeating the purpose of doing things asynchronously?<p>And you don't have to nest all those callbacks and write them inline. Rearrange your code a bit.
The ease with which callbacks can be created leads people to create them carelessly and excessively. While I like what the article has to say, there are ways to write callback heavy code that do not get ugly so fast. Looking at the iOS nested block example from Marco Arment -- the first step is to not do everything inline. Then the code suddenly becomes clear and the argument becomes one of syntax sugar.<p>Comparing callbacks to goto is a tad unfair. They don't merely solve async issues, but also event handling and dynamic systems to name two common uses. I don't see a better solution on the table. Using callbacks to write deeply async code is the real problem. And while async/await may help with this problem, it still won't tell you why step 3 never finishes, because it's still waiting for a come from.
Here's a cool way to reinvert control<p><a href="http://blog.sigfpe.com/2011/10/quick-and-dirty-reinversion-of-control.html" rel="nofollow">http://blog.sigfpe.com/2011/10/quick-and-dirty-reinversion-o...</a>
"I have just delegated the bookkeeping to the compiler."<p>That's not obviously a good thing. Debugging the compiler (or just figuring out why it did something, even if correct) is far more difficult than debugging application code. Given the choice between implementing behavior with application code (or a library function) or adding semantics to the language, I prefer the former because it's much easier to reason about code written in a simple language than to memorize the semantics of a complex language.<p>[edited to replace sarcasm]
callbacks are like goto in that you can create terrible code by using them badly, but also they are vital to implementing good code. if, for, while and co are all syntactic sugar for correct and standardised use of goto with hints to help the compiler make optimisation.<p>in both cases though we don't something universally evil or bad - just something that bad programmers can and will abuse.
So... This article is basically saying that blocking style programming is a lot easier to read and write, and proceeds with demoing a lib which makes async calls look sync. So instead of doing this in $lang, why not invest time in making blocking style faster on the kernel level? Perhaps introduce actors or tasks in the kernel, so that every lang can benefit.
shenanigans. Pyramids (at least in js) can be easily avoided by simply naming your functions. Treating them like the first class objects they are. Naming the function means you are no longer going to an arbitrary code block, but instead going to a concept whose name, doc string, and (through hoisting) position on the page illuminate its purpose.<p>Callbacks aren't bad. Pyramids are bad. Stop writing pyramids.<p>Nodejs also establishes a nice api for callback functions-- in particular, callbacks are defined with an `error` and a `data` argument. You handle the error if it is non-null, otherwise execute `data`.<p>If you want to avoid callbacks, Node also provides event emitters, and streams. Streams in particular provide a nice api for dealing with event based programming.
I think this article misses a main point and that's the fact that all await does it take a function that used to be asynchronous and makes it synchronous. While yes, there are definitely use cases where that is nice, in general I think that if you want to use an await command, why are you making a call that was meant to be async? You are defeating the whole point of async calls.<p>Yes I know a lot of standard libraries have calls that are async and you may not really need for them to be async but I don't think that this is the case often enough that we should abandon callbacks and the like and go back to an age where all code must be synchronous. I know the author isn't saying it to that extreme necessarily, but his comparing callbacks to gotos is extreme as well.