
Async and Await - okket
https://zeit.co/blog/async-and-await
======
agentgt
Personally I'm a huge fan of Observables or (Singles which is a single item
Observable).

One of the most confusing things with Promises vs Observables or any sort of
deferred value is whether it is " _hot_ " or " _cold_ ".

Most deferred objects like Futures in Java and Promises in other languages are
_hot_ which basically means something is already running in the background.

There are many reasons why I prefer the _cold_ model but mainly boils down to
be more explicit, better composition, backpressure handling, and overall
better predictability.

I wish articles talked about this a little more of _hot_ vs _cold_.

~~~
inglor
Observables are actually terrible at backpressure as they're one of the most
eager models around - once you have subscribed to an observable _it_ decides
when to push and how much and you can't handle backpressure very well.

Quoting Erik Meijer who invented Rx Observables:

> My opinion about back pressure is that you either have a truly reactive
> system where the producer is fully in charge and the onus is on the consumer
> to keep up; or a truly interactive the consumer is fully in charge and the
> onus is on the producer to keep up. This is one of the cases where having
> different types of collections makes sense (just like we have different
> collections like lists, maps, sets, ... with different performance
> characteristics and different operations)

You probably want an async iterator for that

~~~
bad_user
You've got certain misunderstandings there. Observables aren't _eager_ , what
you want to say is that they are push-based.

But that doesn't mean they are terrible at _back-pressure_ , in fact they can
be better at back-pressure than any pull based model you can think of, because
the communication is more efficient. Back-pressure is the scope of the
[http://reactive-streams.org](http://reactive-streams.org) protocol and I've
also built a library inspired by Rx that has back-pressured observables,
started before RxJava went this route as well:
[https://monix.io](https://monix.io)

Funny enough back-pressure doesn't help with just "slow consumers". In Rx.NET
flatMap is an alias for mergeMap because it is safer than concatMap, as
concatMap would require buffering without back-pressure. Introduce back-
pressure in the protocol, and concatMap becomes safer and much more efficient
if done wisely. Which is cool because it is concatMap that's the monadic bind.
And many other operations become more efficient and easier to implement
because you no longer need to do buffering all over the place.

In terms of those AsyncIterators, or whatever they are called, I tried that
route but the model is terrible as you're forced to introduce asynchronous
boundaries on each pull. This means the model is very inefficient.

And I'm going to make another claim. Erik Meijer doesn't know what he's
talking about in this case, in spite of how awesome he is and this opinion is
probably the reason for why the Rx.NET library is trailing behind most Rx
implementations. If I'd have to use .NET for a project, I'd probably end up
reimplementing Monix, as Rx.NET isn't suitable for my use-cases at all.

~~~
inglor
No, they're eager - I had this mixed up too until Erik Meijer set me straight:

> Now back to your question. I do not understand what you mean by “observables
> being fundamentally lazy”. I’d need to understand what you mean by that
> before I can give a coherent answer. Iterables are essentially lazy, because
> nothing happens before you call “moveNext”, but I’d categorize observables
> as (hyper) strict since “onNext” gets called wether or not the consumer
> wants it (so to speak).

~~~
agentgt
I think I understand the eager confusion. You mean eager because it is a push
model and not a pull model like iterator which is lazy.

They are eager once _Subscribed_. The lazy part that I and the previous poster
was alluding to is that most observables do nothing till they are subscribed
to unless they are hot where as a Promise and Future are executed/scheduled
immediately and the value is pushed/pulled (respectively).

I totally agree that Observables are not that great for backpressure but
really there isn't a silver bullet on that one regardless (many smart people
have tried to come up with solutions that will work across domains but have
failed). As you mentioned you just have to pick the right tool for the
job/requirements. I for one prefer a broker ACK model like RabbitMQ but many
do not like queues as it just shoves the problem some where else.

------
inglor
> When you use a Promise and don't attach an error handler, in many
> environments you might never find out about the error.

I specced and Node added the `unhandledRejection` hook over a year ago:

process.on("unhandledRejection", (err) => { // handle });

And in browsers Domenic worked on something similar which was added later
(because browsers) but will hopefully be standard in all browsers soon
(Chrome, since 49):

window.addEventListener("unhandledrejection", (err) => {

});

If you use bluebird - you get that up to IE7.

You mention that - but it's worth pointing out upfront it's a solved problem.

Also:

> The problem is that once again, Promises will swallow subsequent resolutions
> and more concerningly, rejections. There might be errors that are never
> logged!

This is arguably a much better design than the alternative - which is to
violate the "callback" contract.

~~~
Rauchg
I'm glad `unhandledRejection` exists (and I thank you for your work).

Unlike Chrome, without setting up the handler _manually_ , you still don't
find out about unhandled rejections. Maybe it's something we could enable
during development (`NODE_ENV` = 'development' perhaps).

~~~
inglor
I apologize for that and take fault and responsibility, I have a PR waiting on
that:
[https://github.com/nodejs/node/pull/6439](https://github.com/nodejs/node/pull/6439)

I'll try to get to that that during the weekend and I humbly apologize :(

------
inglor
> In order to maximize your usage of this feature, you'll want to use modules
> from the ecosystem that expose Promise instead of just a callback.

Bluebird which is one of the most popular promise libraries converts entire
callback APIs to promises in one go:

Promise.promisifyAll(require("fs")); // fs is now promisified and methods
return promises.

(full disclosure, I'm a bluebird contributor)

~~~
Touche
Can I import promisifyAll without bringing all of Bluebird?

~~~
inglor
promisifyAll is _blazing_ fast because it does this:
[https://github.com/petkaantonov/bluebird/blob/master/src/pro...](https://github.com/petkaantonov/bluebird/blob/master/src/promisify.js)

It builds a function dynamically and then compiles it and lets the JIT
optimize it - it incurs very little overhead.

This is impossible if your only tool is the promise constructor. That said -
there are packages that do this without bluebird - like thenify.

Also note - you can use bluebird for promisification and native promises for
other stuff if you'd like and it'd work seamlessly as bluebird passes the
Promises/A+ test suite.

~~~
BinaryIdiot
> promisifyAll is _blazing_ fast

I'm not sure I'd necessarily call ```promisifyAll()``` "blazing fast" but in
all fairness it's a cost incurred up front and that's it so it's not that bad.

~~~
phpnode
The call itself is _relatively_ expensive, but as you say it's a one off call
at startup time and the methods it produces are faster than explicit
promisification using `new Promise`, they are blazing fast :)

------
hiphipjorge
What the hell is this?

    
    
      export default function getUsers () {
        return new Promise((resolve, reject) => {
          getUsers().then(users => {
            filterUsersWithFriends(users)
            .then(resolve)
            .catch((err) => {
              resolve(trySomethingElse(users));
            });
          }, reject)
        });
      }
    

There is never a need to write this type of terrible code if you're using
promises. The solution this is not async/await. I agree that async/await and
generators are nice, but saying they solve a problem that is not actually a
problem seems a little silly.

This is typical of many technical blog posts: Overcomplicate a solution that
could be done much more cleanly and clearly in order to show off a new
feature, when in reality that feature is actually not that useful.

~~~
lhecker
And the "correct" code would be this for instance:

    
    
      export default function getUsers () {
        return getUsers().then(users => {
          return filterUsersWithFriends(users)
            .catch(err => trySomethingElse(users));
        });
      }
    

Or if you like one-liners:

    
    
      export default function getUsers () {
        return getUsers().then(users => filterUsersWithFriends(users).catch(err => trySomethingElse(users)));
      }

~~~
untog
_(EDIT: ahem, that 'll teach me to not look at the code I'm editing)_

~~~
davej
This isn't correct, `users` would be undefined.

------
inglor
> Simultaneously, Observable continues to make progress within TC39 to become
> a first-class construct of the language.

Worth pointing out that the TC discussed observables with the cancellation you
describe here and rejected them less than a week ago. Observables did not
progress to stage 2. Currently we are bikeshedding design at the es-observable
and es-cancel-token repos (under zenparsing) - contributions are welcome.

Hopefully, a compromise everyone is happy about will be reached soon.

You also might want to mention async-iterators which are a stage 2 proposal.

~~~
egeozcan
Links to mentioned repos:

[https://github.com/zenparsing/es-
observable](https://github.com/zenparsing/es-observable)

[https://github.com/zenparsing/es-cancel-
token](https://github.com/zenparsing/es-cancel-token)

------
artursapek
You can use async/await to implement a Mutex in ES6! It's crazy.

[https://gist.github.com/artursapek/70437f6cdb562b7b62e910fd3...](https://gist.github.com/artursapek/70437f6cdb562b7b62e910fd33ee8554)

Usage:

    
    
        import Mutex from 'lib/mutex';
    
        class MyObject {
          constructor() {
            this.m = new Mutex();
          }
    
          async someNetworkCall() {
            let unlock = await this.m.lock();
    
            doSomethingAsynchronous().then((result) => {
              // handle result
              unlock();
            }).catch((err) => {
              // handle error
              unlock();
            });
          }
        }
    
        let mo = new MyObject();
        mo.someNetworkCall();
        // The next call will wait until the first call is finished
        mo.someNetworkCall();
    
    

I love it. We use it in our production codebase at Codecademy.

~~~
spriggan3
Why do you need mutexes in a single threaded context ? If you need sequential
async operations that's what async await are for. What if you need to return
something from someNetworkCall ? yes you're f..cked in that case.

~~~
phpnode
Presumably instances of `MyObject` can be accessed by functions which are
running while this function is suspended, waiting for the network call. It's
single threaded, as in only one thing happens at a time from JS's point of
view, but other functions can be invoked (by a request coming in, or a UI
event etc) while an asynchronous operation is awaiting completion.

------
korkota
If you wanna have some fun with async/await, I recommend you try Dart.

[https://www.dartlang.org/docs/tutorials/futures/](https://www.dartlang.org/docs/tutorials/futures/)

[https://www.dartlang.org/articles/await-
async/](https://www.dartlang.org/articles/await-async/)

[https://www.dartlang.org/articles/beyond-
async/](https://www.dartlang.org/articles/beyond-async/)

I have done simple multithreaded chat a few month ago, that was interesting (:

------
BinaryIdiot
This is a good overview of promises and async / await. The initial thesis of
this seems to be that you need promises or async / await to avoid callback
hell which I disagree with. You can still write good code, not use promises or
async / await and not run into callback hell.

The example given in the article of callback hell isn't even a very good one.
Why are we using an asynchronous method to fetch users, another asynchronous
method to filter them and yet a third one to get their likes and return those.
If that's the ultimate goal then, depending on your backend, you can should be
able to make that _in a single call_.

But let's ignore the poor example in the article. Let's say we need to make 3
calls to 3 asynchronous functions and the result ultimately needs to be
returned after all 3 functions are finished executing. Do you just nest them
like the article? Absolutely not.

Essentially my point is: you can _always_ architect around callback hell and
make it better. I've already gotten into projects where I would now consider
them "promise hell" where every method returns a promise of a promise of a
promise of a promise of a promise and it's just maddening to debug.

Alternatives to callback hell are always context specific. There is no one-
shot-cure-all, in my opinion (not even promises or async / await) but there
are plenty of steps you can take.

\- Can I separate or combine at least two of these asynchronous functions?

\- Can I use a pseudo parallel or serial processing pattern ala async.js?

\- Can I use a messaging pattern where each message is handled separately with
a single callback ala msngr.js or postal.js?

\- Can I simply create a response building pattern where many asynchronous
methods write to the same response object and, when it's complete, return it?

\- Does it make sense to use a promise here? What about the async / await
pattern?

~~~
recursive
I'm pretty new at promises, but doesn't any appearance of a "promise of a
promise" indicate a trivial mis-use of promises? Isn't the whole point of
promises that they can be chained together without nesting?

~~~
vog
Indeed. And if you generalize this idea only slightly, you get ...... monads!

Maybe promises will be the vehicle that makes understanding the usefulness of
monads available for the masses.

Alternatively, promises may fail the acceptance test for the same reason as
monads - because the average developer simply doesn't care / doesn't get it.

I really hope for the first scenario, though.

~~~
pekk
If you are not trying to use Haskell (which has monads as a workaround for
certain problems) then what is the point having monads? The average developer
doesn't care because they are working in an environment where monads aren't
needed.

Trying to drag monads into arbitrary languages like Javascript isn't some kind
of best practice, it's cargo culting.

~~~
WorldMaker
Monad is a mathematical term for certain forms of (categorical) abstraction.
Yes, Haskell actively uses the term "monad" in the language itself to discuss
these things, but that doesn't make them a "Haskell thing" any more than
Fortran has mathematical formulas and thus all math is a "Fortran thing".

Monads are also not a "workaround for certain problems", contrary to what you
hear sometimes, but an attempt to generalize a number of seemingly unrelated
concepts into a uniform interface.

Uniform interfaces are good for programmers. This is the magic that monads
bring to other languages as we get a handle of what the monad is for and what
it does. You don't have to know what a monad is to make use of it. You don't
need to know that Promises are a "continuation monad" to take advantage of the
fact that async/await are simple tools to transform the "continuation monad"
into something resembling synchronous code, which is a big win.

(Meanwhile Haskell points out that you can go simpler than async/await for
your monad bindings if you like and use the same constructs for other monads
too, but language developers are taking this one step at a time and trying to
hit a happy medium where they have the power that monad binding gives but the
discoverability and debuggability of more bespoke solutions to individual
monads...)

Needless to say, it's not cargo culting so much as learning more about the
very mathematical fundamentals of programming and using those fundamentals to
build better languages with better features for better programs.

------
phpnode
I think your examples could be simplified:

    
    
        export default function getUsers () {
          return getUsersFromDB()
          .then(filterUsersWithFriends)
          .then(getUsersLikes);
        }
    

And there's just about never a reason to use the `Promise` constructor in real
world code. If you already have a promise as in your example, so you can
rewrite your refactoring as:

    
    
        export default function getUsers () {
          return getUsersFromDB()
            .then(users => filterUsersWithFriends(users).catch(err => trySomethingElse(users)));
        }

~~~
krisdol
> And there's just about never a reason to use the `Promise` constructor in
> real world code

Why not? If I want to wrap a callback function in a promised sequence, I don't
see a way around it. Please don't suggest importing bluebird/promisifyAll
because that only teaches me how to avoid thinking about the problem, not how
it's actually solved, let alone why it's a problem to begin with.

    
    
      function promiseMeFoo() {
        return new Promise((resolve, reject) => {
          asynchronousFoo((err, out) => {
            if (err) return reject(err);
            return resolve(out)
          });
        });
      }
    

^ aside from just rearranging the code for legibility, how can I do the same
thing without constructing a promise explicitly?

~~~
phpnode
Bluebird's `promisifyAll()` is just a way to generalize that pattern, so that
you don't need to write it yourself. And if you care about performance it's
substantially faster too. If you want to learn about the pattern then fine,
but personally I don't want to write that for every single `fs` function I use
in a project, it's much more convenient to just
`Bluebird.promisifyAll(require('fs'))`.

~~~
WorldMaker
Also libraries like `promise-ring` that just do this one thing without needing
all of bluebird installed.

------
mkozlows
The "problems with callbacks" section of this is loaded with FUD. I mean, the
actual problems they list are almost all minor or easily solvable.

1\. Repetitive error-handling, because you usually just want to pass it along.
Maybe. Or maybe not. Forcing you to actually think about how you want to
handle each error might be too cautious, but it's defensible -- especially in
the context that async stuff is generally I/O related, so it's more likely
than regular method invocation to need a close or a cleanup or a rollback in
an error case.

2\. The possibility for introducing error by forgetting to return from an
error-handling callback pass-along. This is trivially catchable with a linter.
(Eslint has a callback-return rule for this purpose.)

3\. The idea that callbacks might be called multiple times. When that happens,
it's not some weird case where maybe your callback needs to be prepared for
multiple invocations, it's just a bug in the function you're calling. It
should never happen with solid library code.

4\. That the error parameter of a callback might be any kind of falsey value.
This is true, but not a problem. Don't test for null or false, test for
falseyness.

As much as people talk about "callback hell," it's never clear to me what
problems they actually think they're having. It seems that as often as not,
it's just a visceral dislike of indentation.

~~~
danneu
Most examples of callback hell on blogs and forum comments are just linear
pyramids where each callback neatly waterfalls into the next. They are
manageable but ugly which I think is why it's common to dismiss promises/await
as cosmetic.

In practice, callbacks become a problem when you need conditional async logic.
Like if you have your simple callback pyramid but want to fork the chain at
some level and merge it back at the end.

What would've been a basic if-condition with promises/await is often an
invasive change with callbacks where your only tool is to extract and rewrite
logic into helper functions. A simple conceptual change easily blossoms into
heavy indirection where taming the complexity becomes an engineering feat.

At the end of the day, we all have different appetites for trade-offs.

~~~
mkozlows
That's definitely a better example, yeah. I think that it's a very manageable
problem -- if your functions are small and compositional in the first place,
you're not going to be refactoring your 100-line magnum opus in a major way
when you introduce a new conditional bit -- but that's definitely at least a
problem that rings true to me in the way that so many "callback hell"
complaints don't.

------
iLoch
I do sort of wish we had something better than promises. Errors can be
swallowed and there's no way to cancel promises which makes sense, but is also
problematic when promise based APIs like Fetch exist. You also can't indicate
progress in a promise, which makes them a poor choice for many asynchronous
processes (unless you attach a callback as well.)

I like the idea of Observables though. If they could work implicitly with
async/await I'd be sold.

~~~
inglor
Most of those are FUD:

Promies can be cancelled - for example bluebird promises. Standard promises
haven't added that feature and are going for tokens - but look at
[https://github.com/domenic/cancelable-
promise](https://github.com/domenic/cancelable-promise) and
[https://github.com/zenparsing/es-cancel-
token/](https://github.com/zenparsing/es-cancel-token/) \- both stage one
proposals.

Fetch will take a cancellation token - it's just taking a while but work is
being done. A lot of discussions are happening since people are scared
(rightfully so) about making a new API bad from the start.

The "errors are swallowed" is also FUD, see my comment below about
`unhandledRejection` and `unhandledrejection`.

~~~
iLoch
Don't get me wrong, I use async/await almost exclusively these days - but I've
run into these problems in large real world applications, and they can be hard
to fix.

This isn't FUD if the main implementation is "broken".

With regards to errors being swallowed, I'm not talking about unhandled
rejections, I'm talking about code that mishandles rejections and may swallow
one intentionally, or code that doesn't pass along errors properly.

------
fizzbatter
Wow, can i just ignore the article for a second, and say that i love the
Markdown.. meta thing going on? That is to say, the Markdown is totally
visible, unchanged, but still rendered as bold/header/etc.

I'm going to have to use that i think. Looks great!

~~~
spatten
I agree, it looks great. Funnily enough I didn't even notice until you
mentioned it.

However, I have to pedantically point out that the Markdown is not 100%
visible. The author made the correct choice and just showed links as links
instead of the raw Markdown syntax.

~~~
fizzbatter
Good point! For what it's worth, i'll have to play with that. Using the real
markdown sounds very appealing.

------
tlrobinson
I love async/await, it's the future, but dear god debugging transpiled
async/await code is a nightmare (and I'm no stranger to JS transpilers, been
doing it since 2007)

I'm not sure if Babel/Webpack doesn't support source maps with async/await, or
if I don't have it configured right, but if someone has gotten it working
please let me know how.

~~~
hokkos
Activate the async-to-generator plug-in during the development, it is a
simpler transformation so have a massively better source map support, but
still have some difficulties to place some breakpoints. Sadly uglify still
doesn't support generators so we can't use it in production mode.

------
Rezo
Is multi-catch or typed catch statements anywhere on the ES roadmap? These are
really convenient, essentially you do:

    
    
      DB.insert(obj).then(result => {
        ...
      }).catch(UnauthorizedError, e => {
          // Will end up here if auth failed
      }).catch(UniqueConstraintError, e => {
          // Will end up here if there's a conflict in the DB
      }).catch(e => {
          // Generic catch-all
      });
    

The above is at least available in the Bluebird promise API and greatly
improves code organization and readability in my opinion.

------
mappum
I wrote a module that provides something similar to this using ES6 generators
(which are already widely supported), but also supports callbacks in addition
to promises. This way, you can make calls to existing APIs without having to
wrap them to return Promises.

[https://github.com/mappum/watt](https://github.com/mappum/watt)

Since it uses generators, you use 'yield' where you would have used 'await'.

~~~
jamesroseman
I'm not trying to be antagonistic, but I am honestly curious how this
differentiates from tj/co?

[https://github.com/tj/co](https://github.com/tj/co)

~~~
mappum
From the bottom of the watt README:

co is similar to watt, but the main difference is that it only works with
Promises. It requires that you convert callback functions to return Promises
before you can call them, and it does not let you wrap generators with a
callback API.

------
kyberias
Is this async/await more or less directly lifted from C# or what is the
ethymology?

------
divkakwani
Check out the book You Don't Know JS ([https://github.com/getify/You-Dont-
Know-JS/blob/master/async...](https://github.com/getify/You-Dont-Know-
JS/blob/master/async%20&%20performance/README.md#you-dont-know-js-async--
performance)) for more details on Promises and, in general, on asynchronous
programming.

~~~
billmalarky
I'll plug "scopes & closures" and "this & object prototypes" as well. Fairly
quick reads and you'll understand JS better than your average developer
afterwards.

~~~
couchand
Kyle Simpson is crazy. Read the first third of any of his books and then put
them down before you get to the part where he goes off the rails. The "Scopes
and Closures" one is particularly ridiculous... He goes off on a tangent about
how let bindings should be written with a specific syntax and don't worry he's
written a transpiler for it so you don't have to bother using the standard.

~~~
billmalarky
He can get preachy about his preferred (subjective) method of solving some
problems, but you can't deny he does a good job of explaining the fundamentals
in an easy to understand manner. That is, it doesn't feel like reading a
textbook.

Bottom line, the books have a lot value in them for the time investment it
takes to read them.

------
maxxxxx
How do people handle shutdown of applications while async/await stuff is still
executing? I do a lot of desktop app and need to stop executing when it
closes. With the async stuff it's really hard to tell what's still in the
queue to be executed and should be waited for.

Is the whole pattern more for server stuff where you can assume there will be
no shutdowns?

~~~
pkaler
If you're on OSX/iOS you would use an NSOperation and NSOperationQueue.

Your NSOperation objects would conform to the NSCoding protocol. Then you
would iterate through NSOperationQueue.operations and use NSKeyedArchiver to
serialize.

------
dcposch
Sadly async/await, like so many ES6/ES7 features, adds needless complexity.

You can already achieve the exact same thing with generators. Check out the
popular `co` module.

Before:

    
    
      function getLikes () {
        getUsers((err, users) => {
          if (err) return fn(err);
          filterUsersWithFriends((err, usersWithFriends) => {
            if (err) return fn(err);
            getUsersLikes(usersWithFriends, (err, likes) => {
              if (err) return fn (err);
              fn(null, likes);
            });
          });
        });
      }
    

After, with generators (already available today):

    
    
      getLikes = co(function*() {
        const users = yield getUsers();
        const filtered = yield filterUsersWithFriends(users);
        return getUsersLikes(filtered);
      })
    

After, with async/await (ES7, coming soon):

    
    
      function getLikes () {
        const users = await getUsers();
        const filtered = await filterUsersWithFriends(users);
        return getUsersLikes(filtered);
      }
    

\---

The first example is terrible and the second two are good. Is that last one
really worth adding yet another language feature and two more keywords?

This is the sad thing about big committees with lots of corporations on them:
everyone "representative" wants to report back to HQ that they had "impact" on
the future standard, they want their fingerprints in there in some way.

That's how standards grow warts and features and keywords and competing module
systems until they're a hairball.

Here's what John Resig, creator of JQuery had to say about it. It's short and
funny and sad

[https://twitter.com/jeresig/status/707015494956621824](https://twitter.com/jeresig/status/707015494956621824)

------
sktrdie
I don't really like _await_. It seems to be a syntactical fix which acts in a
quite magical way. Not sure if introducing magic to a language syntax is what
we want. Rather, we should think about data flow of our async apps
differently. I believe Observables are the future of async code. Once you
grasp the concept that everything in your app can be transformed into streams
of events, and you work with these events using chains of .map(), .filter(),
.reduce(), etc. you'll grasp the true power of this paradigm and how well it
fits async and/or concurrent apps.

I suggest anybody that doesn't quite swallow the await syntax, to have a look
at this presentation about Observables at Netflix as it's quite mind changing:
[https://www.youtube.com/watch?v=XRYN2xt11Ek](https://www.youtube.com/watch?v=XRYN2xt11Ek)

~~~
coldtea
> _I don 't really like await. It seems to be a syntactical fix which acts in
> a quite magical way. Not sure if introducing magic to a language syntax is
> what we want._

You mean like having "for", "if/else", "functions", etc, instead of just
"load" to some address and "jump" to an instruction?

There's nothing magical about async/await.

The main people I know that like programming with callbacks directly is people
who only first learned async programming in JS (and thus don't know the
relevant PL history).

~~~
sktrdie
await feels more magical than other language constructs because, at least to
me, async constructs are much more complicated than synch ones (if/else,
functions, for, etc). A quite important dimension that this await/async syntax
adds is time. Now the programmer is forced to think about time, even though
the code is meant to read sequentially.

For instance what is `return await asynchronousOperation(val);` meant to
return? You'd think that a return always returns something immediately -
however when reading this code your mind has to process also the await keyword
and think about the extra time dimension.

With a callback you have clearer separation of concerns - you know you're
passing the behavior (the function) as an argument that will be executed at
some other moment in time.

This async/await stuff seems like a fix for "ok JS code is becoming callback
hell, how do we fix it?", rather than thinking about what async programming
actually means and how we can reason about it more naturally - for instance I
don't want to code async stuff and have it look like it's sync, because well,
they're different concepts!

~~~
coldtea
> _for instance I don 't want to code async stuff and have it look like it's
> sync, because well, they're different concepts!_

They shouldn't be though.

In the ideal case, code should be able to function 100% whether it's a sync or
async operation -- kind of how promises (and async with promises) works. The
async-ness should could just be a deployment/configuration detail.

------
dreamdu5t
Don't wait for the JS community to slowly realize monads. Use PureScript today
and stop worrying about callbacks.

~~~
spriggan3
Are you saying "stop using NodeJS with Javascript", use a third party language
that compiles to Javascript ? What's the point using NodeJS at first place ?
Better use Haskell directly...

~~~
bbcbasic
> What's the point using NodeJS

Good question.

------
danjc
Isn't it ironic that for a language that has new frameworks popping up all the
time, the guts of it evolve so slowly?

I primarily work in js and c# so I can't speak for other languages but
async/await in c# is incredibly useful and has been around for years. Js feels
like it lags a long way behind.

~~~
whatever_dude
I think it's more or less obvious that languages and platforms ran by a
benevolent dictatorship can move much faster than one ran by a committee of
several different groups and companies.

This is especially true of JavaScript, where maintaining backwards
compatibility is really important [1] -- you have to avoid doing something you
can't change later.

They also tend to wait until there's a few implementations of any proposal
already. It has to be tested first, before becoming standard.

[1] [http://www.2ality.com/2014/12/one-
javascript.html](http://www.2ality.com/2014/12/one-javascript.html)

------
TheAceOfHearts
I've been using async / await since babel started out, and before that I was
using co with generators.

While I do agree that it's a big improvement, it sucks to see people using
try/catch for flow control. This is really a problem with Promises in general:
you can't easily differentiate between exceptional behavior and failures.

After playing around with Elixir for some smaller projects, I've really been
enjoying their approach: you return a tuple with :ok and the result or :error
and the result. And they ALSO have exceptions, but they're not used for flow
control, you only get exceptions for truly exceptional behavior.

------
drostie
Note that this is already possible in ES6 with generators and yield, if you
write your own trampoline:

[https://gist.github.com/drostie/66876fc880cb17c2a6763968aef1...](https://gist.github.com/drostie/66876fc880cb17c2a6763968aef1040d)

Of course, it's nice to get this sort of native support for trampolining, I
just wanted to be the killjoy who pointed out that as of ES6 you can already
write the same thing in a slightly different way, with functions that only go
up to maybe 30 SLOC or so each and therefore don't kill your codebase.

------
beck5
Although async and await do look great, I am not very keen on using try/catch
for everything. Is this not going to end up with more verbose code? I
appreciate it will be impossible to ignore errors though.

~~~
billmalarky
In my experience async/await style code is _much_ less verbose than callback
style code. Like 50% less. Async/await style is also _much_ easier to follow,
especially for non-node developers, since it makes asynchronous code _appear_
as if it is synchronous.

That said I like to use the caolan/async library when dealing with async
callbacks and that adds a ton of verbiage overhead in exchange for a more
understandable flow of the data (in my opinion).

~~~
Bahamut
The async.js library has awful semantics - promises are an improvement in
every way compared to it, whether it is reasons of flow, readability, and even
perf (bluebird is quite faster).

The catchall try-catch is my major dislike for async-await though. The only
way to exit the async-await loop being to throw is overkill, and makes it more
complicated to localize different desired flow branching.

------
m1sta_
I'd love for syntactic sugar to mark a function, arrow or otherwise, as
returning a promise. Reject and resolve could be standard, hidden, context-
specific variables, just like arguments and this.

------
jdauriemma
Here's a nice writeup that's critical of async and await:

[https://blog.getify.com/not-awaiting-1/](https://blog.getify.com/not-
awaiting-1/)

------
sigill
> Not only is it [linearly written code with async/await] easier to read (as
> the chaining example was), but now the error handling behavior is the exact
> same as regular synchronous JavaScript code.

Then why not write code synchronously in the first place?

I understand that async/await looks attractive in languages with an event loop
model, such as Javascript, or with expensive threads, such as C#.

I see two main justifications for async/await in these languages: 1\. Linear
code. 2\. More performant code.

It's easier to follow a linear flow of code than it is to follow entangled
callbacks and state machines.

Async/await is also sometimes perceived to help with performance. This is an
observation that is typically made in languages with few, heavy threads, such
as C#. As async/await code "doesn't block", expensive thread resources are
mostly free to execute concurrent async/await code.

It seems to me that async/await provides very little apart from making code
flow over callbacks look like regular, synchronous, structured, procedural
code.

It introduces new concepts: Now there are two types of function calls: vanilla
"blocking" function calls and the new async function calls. The difference is
that one "blocks" or "takes a long time" whereas the other does not.

Some APIs are only available in one flavor. How do I adapt between the two
conventions in code that is required to deal with both API types?

C# and the .NET framework mostly choose to _completely double API surface_ \-
for example you now have a System.IO.Stream with both vanilla and async/await
Read()/Write() etc. methods. You can only hope that they're implemented in a
consistent fashion. (they're not)

Given all these conceptual and practical disadvantages I don't understand how
async/await becomes such a praised thing.

I'm in favor of coroutines/green threads/goroutines (depending on whether you
learned programming in the 1970ies, 1990ies or 2010ies ;)) with a scheduler
that multiplexes I/O to the appropriate OS concepts for concurrency. With this
approach you get the benefits of structured, procedural programming without
the mental and practical disadvantages of async/await.

------
hippich
"callback hell" is just a sign that you did not split your code into small
functional chunks, nothing more. promises/async/await might have its place,
but "callback hell" is developer's fault, who can't shake off linear coding
past, not language's.

~~~
spriggan3
Relevant article :

[http://journal.stuffwithstuff.com/2015/02/01/what-color-
is-y...](http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-
function/)

The callback hell is here no matter how hard you try to hide it under some
syntactic sugar. That's what makes Go CSP superior to Async IO by the way.

~~~
hippich
I am not talking about hiding anything. If anything, promises/async/await IS
hiding.

When I see "callback hell mumbo-jumbo" \- it just takes some time to untangle
it, assign proper context to each function, and flatten out it inside proper
object to keep that context in. And instead of anonymous functions in
callbacks you just use object methods.

Bonus: you got something that is so easy to unit test.

I see a lot of callback-hellish code, but it comes from people who's
experience was in python/java/php/etc, where typically you write linear code
(threads are also linear code in essence) And in JS you have to think about
frames of code - this is a bit different, but it is not a bug, it is a
feature.

------
raimille1
Great article! Reminded me of this jewel: [https://pouchdb.com/2015/05/18/we-
have-a-problem-with-promis...](https://pouchdb.com/2015/05/18/we-have-a-
problem-with-promises.html)

Great explanation as well.

------
sscarduzio
After 2 years of FP in Scala, I'm amazed to see at what lengths OOP engineers
go to circumvent learning what monadic composition is (including reinventing
it).

Go ahead guys, in another 6 months you'll reinvent the for comprehension! ;)

~~~
deepsun
You forgot about time dimension, it's a big deal. Take a look at RxScala --
they wouldn't go implementing it if it was already included in Scala :)

But yeah, Rx makes handling streams of events kinda similar to handling
collections of objects, which is cool. Except for time dimension
(`.debounce()`, `.throttle()` etc) which, as I said, is a big deal.

------
davidw
For people who know both systems well, how does this compare with Erlang? The
await thing superficially looks a lot like Erlang waiting for a message from
another process.

~~~
barrkel
Await typically uses a CPS transform to rewrite methods into continuations.
Effectively the remainder of the method after the await is passed as a
callback to the async operation. The async operation can resume when it
completes.

Erlang's waiting will interact with the VM's scheduler, and pause the green
process until a message is available. Logically though, there's little
difference between a stack treated as data by a scheduler, and the closure
captured by the continuation after the CPS induced by await. The chief
practical difference is a continuation could be called more than once.
Resuming a green process with the same stack location more than once won't
work very well because the stack gets reused. But you could get around that by
storing the stack not as a reused vector, but as a linked list of heap-
allocated activation records (an exact parallel to nested closures, which
happens in anything beyond the most trivial CPS transform). Erlang wouldn't do
that because (a) it's slower and (b) it's not necessary since it can't
continue more than once.

(I implemented anonymous methods in the Delphi compiler and got intimately
familiar with lots of these details.)

~~~
nialv7
I believe await uses generators instead of CPS

~~~
barrkel
Generators are implemented with continuations, just like C#'s iterators (yield
return statement). In C#'s case, it rewrites the function into a continuation
via a state machine, but it's just using an integer to point at the remainder
of the function, just like an index into an array isn't very different to a
pointer - it's a distinction without much of a difference.

(JS VMs are free to implement generators however they like, of course.)

------
educar
While async and await is a welcome change, callback hell is mostly a symptom
of the programmer's own doing. It is possible to have callback hell in any
async programming stack/framework.

In C++, you usually don't write anonymous functions and lambdas at will. You
make proper named functions out of them and write unit tests even. Same
applies to js. It's really just a matter of discipline and having a culture of
writing manageable code.

If someone is curious how this can be done, please post a link here and I can
help clean it up.

~~~
Kequc
I've got a classic callback hell type situation that just cropped up in one of
my applications. We are using an image library to change an uploaded file into
a background image for the header.

[https://gist.github.com/Kequc/bcd80980184ed480b864581bcdb351...](https://gist.github.com/Kequc/bcd80980184ed480b864581bcdb351f4)

I've toyed with separating each stage into sections, but I still need a
callback to return at the end of it. Which seems to necessitate an anonymous
function every step of the way.

What is your proposal.

~~~
educar
Something like this.

    
    
      function resizeImage(req, res, next) {
          lwip.open(req.file.buffer, 'jpg', function (err, img) {
              if (err) return callback(err);
    
              var image = new Image(img);
              let ratio = (image.width() > 960 ? (960 / image.width()) : 1);
    
              async.series([
                  image.scale.bind(image, ratio),
                  image.crop.bind(image, image.width(), Math.min((image.width() / 2), image.height())),
                  image.write.bind(image, req.params['id'], "TITLE_IMAGE", buffer, "image/jpeg")
              ], next);
          });
      }
    
      router.post('/:id/title-image', function (req, res, next) {
          resizeImage(req, res, function (err) {
              if (err) res.sendStatus(400);
    
              res.sendStatus(200);
          });
      });
    
    
    
    

This is just my "way" of writing things:

1\. I write with 4 spaces because it lets me know when I should refactor my
code into something more readable.

2\. I usually move route handlers to it's own file. So it will read like ```
router.post('/:id/title-image', routes.resizeImage); ``` The idea is that
routes code simply goes through the model code and send the right http status
codes.

3\. async is your friend :-) In general, I try to model my model APIs with
async in mind.

I can give it a better shot later today if you had any comments on my first
try.

~~~
Kequc
I've amended `try1.js` and `try2.js` to my gist.

[https://gist.github.com/Kequc/bcd80980184ed480b864581bcdb351...](https://gist.github.com/Kequc/bcd80980184ed480b864581bcdb351f4)

I'm not sure async is my new favourite library. It seems to obfuscate a lot of
behaviour out of my hands, then it returns the result of each function as an
array. During processing it doesn't remain clear to me what the value of
`image` is. It appears like the crop operation is being applied to the
original image rather than the scaled one.

That said - it does seem to work?

Another alternative, since I am calculating the dimensions of the image to
start out. Something that is easier to perceive once the code is a bit more
organised like this. Is to use the library's built in `batch` functionality.

It wasn't possible because I didn't have the image dimensions up front. But
now I do. So I used that approach instead of async in `try2.js`. This refactor
doesn't help in situations where the result of the previous callback is
important.

Would a Promise be the best fix then? Promises seem to do the same thing as
async but in a more javascripty way... hey, you said no need.

------
framp
The problem with async is that you have to wrap everything in try/catch

------
fapjacks
And not one person mentions publish-subscribe.

------
cutler
Just use Elixir/Erlang and all these problems vanish.

------
cm2187
This website is crashing chrome. Congratulations!

~~~
Reedx
Looks good here, and the site is even devoid of 3rd party js. Maybe the crash
is on your end. Congrats?

~~~
cm2187
As in like if I made up my own version of chrome?

~~~
mikey_p
Weird edge cases with Chrome are surprisingly common in my experience. We just
relaunched our site after a major rebrand and one user contacted us throwing a
huge fit that our site was terrible and every blog page made Chrome crash.
This was only one user out of 20,000 uniques according to GA, and I'm sure if
we had a widespread problem we would have heard about it from more than one
person.

I've also seen really weird cases of Chrome incorrectly caching stuff, one
time it broke web fonts for a few days (within the last couple of years, no
less), etc.

Chrome updates a lot and to me it seems the stick pretty closely to the "go
fast and break things" motto.

~~~
cm2187
In my case I think I must be stuck in an old version (42) by Corporate IT. But
to crash chrome 42, you have to use some nasty javascript or exploit a
vulnerability. I haven't seen the website (for obvious reasons) but it would
have to be a pretty sophisticated website to justify this kind of use of
javascript. But from the title it looks like just a blog article....

