The first time I heard of async/await was actually with Microsoft C#; so I'm not too surprised to see them prioritize this feature in JS land.
...which is cool, because using async/await in C# made me realize how much simpler dealing with async code could be (compared to my experience with JS callbacks or Java threads).
It was the first interaction we (at Bloomberg) had with async/await as well. We really wanted to see it come to JS and as the spec was evolving it was clear that the path to getting there (if TC-39 ultimately agreed async/await was a desired thing) was going to be via generators. That is how we wound up getting involved with Igalia back in Feb 2013. I kicked off a multi-year project with Andy Wingo / Igalia to direct his mighty keyboard at generators and get them landed in v8. We weren't quite sure TC-39 would agree async/await was a good thing in the end, but it seems now all the pieces are finally coming together. It is satisfying to see the endgame within reach right now and I hope it is worth it once it starts to become more widely available! Companies -- help get spec features implemented! We essentially acted as the bank when it came to the generators work. You don't need to land the code yourself to help move the needle. Thank you Igalia for being awesome.
I know you guys at Bloomberg have also sponsored Igalia to work on implementing CSS Grid Layout as well. Most web devs seem to be unaware, but I assure you those of us who are definitely appreciate it!
Microsoft is also pushing for async/await in C++ in the form of stackless resumable functions complete with an implementation before C++17 has been standardized.
I'm a little sad the async/await model is winning over. I really prefer Go's CSP. It can do everything the async/await model can, plus "streaming" API's. I dig (sorta) C#'s async/await, but I still don't see why it's "winning" over other, better models...
The one advantage of C#'s async/await over Go's CSP is that by exposing the underlying Tasks, you can take more control over how the continuations get executed. The result of this is that it's easy to have async code that all runs on the same thread (usually the UI thread) even while it calls a function makes a network request that gets scheduled to a background thread.
Go's model doesn't give you as much control over which thread a particular operation gets assigned to. It's not theoretically impossible, I suppose, but it's hard to imagine what a simple API for it would look like.
But yes, if this particular problem were solved in an elegant way, I would also prefer the CSP model. It makes it so you don't have to worry about which operations are async or not.
A huge problem with C#'s Async feature that I hope gets resolved (and, obviously that this failure pattern is not ported to Javascript): Explicitly blocking during a function that uses async somewhere inside (a sub-function) will cause your application to deadlock. It is extremely surprising that .NET does not handle this (abet novice) design mistake in a graceful way. (For example, checking if a thread is blocked before attempting to schedule tasks to it)
This was one of my main worries about Go when I evaluated it back in 2010. Most green threads implementations have an explicit determinism model, and it's typically straightforward to reason about and depend on execution order and suchlike, much as you can with plain asynchronous code, but with Go there didn't appear to be any guarantee when or how your code ran.
In any case I suppose many styles of Go program will exhibit accidental determinism, but it still seems too difficult to depend on as randomness can be introduced simply by invoking the wrong function, or by using a different runtime version, which to me feels comparable to the hazards of raw multithreading.
Because the only thing Go did was bake CSP into the language, it is nice, but not a solution for every concurrency problem.
While async/await allows for an abstraction over the lower level tasks, that are still exposed, in case one needs more control over the threading runtime.
For example, in JS's case you can still dig into the Promise world if needed.
Similarly in C#, Tasks / Dataflow and SignalR can also be used.
Whereas in Go, correct me if I am wrong, outside CSP one needs to reach out to threads/mutex/semaphore directly.
> Because the only thing Go did was bake CSP into the language, it is nice, but not a solution for every concurrency problem.
I'm not sure I understand your sentence. Are you saying that communicating sequential process only works because it is first class in Go or that CSP is not a solution to every concurrency problem ?
It is not a solution to every concurrency problem.
I tend to use languages that give library writers (almost) the same control as the language itself has.
EDIT: To expand a little more into what I mean. In most languages that offer async/await, that is mostly syntactic sugar (with some compiler help) to the blessed task pool library.
A good way to explore that is to see the parallel libraries available in C++, JVM, .NET, Haskell and OCaml worlds.
Observables are great, and are indeed the stream equivalent to Tasks. But the await/async keyword compilation doesn't support them. I think that's the parent post's point.
It was during a presentation on the NDC developer conference a few years back, when the feature was new to C#. I remember thinking it was one of the best presentations that year, but right now I don't remember what year it was, and I can't find any past agendas to search either.
NDC posts their videos online after each conference, and I did a quick skim. I think it may be this session here, but I haven't double-checked:
For anyone unfamiliar with JS, async/await is about the best JavaScript will ever get:
var orders = await getJSON('/users/joe/orders');
No callbacks, promises etc (Edit: that you have to look at, as other posters note the implementation uses promises in the background).
You still have to explicitly say you want to be async, so it's not quite as good as non-blocking IO in something like Elixir, but it's the future as far as JS goes.
There are promises there, it just hides it. All async functions return a promise, and `await` works with promises builtin. It's basically the same thing as generators, without the `function*` syntax.
Which is unfortunate because async/await only works with promises, and if you want any other async model (like one that doesn't eat errors all the time) you can't use it. Luckily, generators are fine.
What do you mean by "if you want any other async model you can't use it"? I know you can write whatever code you want, but promises are supposed to be the standard way of signaling completion of asynchronous operations in javascript.
This is the same as saying that "arrow functions only works if you want to represent functions".
Also, promises don't eat errors. It's the code using those promises that forget to handle them.
The async/await fixes that by unwrapping those errors for you. Any errors in an "await expression" will be thrown as exceptions up the stack.
> What do you mean by "if you want any other async model you can't use it"?
Promises are not the only way to do async work in JS, as much as some people like to say that. You have observables and channels, which are more powerful because they handle streaming.
> Also, promises don't eat errors. It's the code using those promises that forget to handle them.
They literally do. They run your code in a try/catch block and store the error away, and you have to remember yourself to manually throw it later. You shouldn't be forced to run code inside a try/catch. http://jlongster.com/Stop-Trying-to-Catch-Me
>I don't care about awkward .then() syntax. I don't mind automatic error propagation. I don't care having to call .done() on a promise chain. I don't care about losing the stack (which is inherent in any async work). I care that promises grab all errors, just like try/catch.
You should stop caring about that and start caring about the other stuff.
"Breakpoint on exception" still works, albeit slightly altered, and in any way it's a minor detail compared to the far better mental model for code execution that async and promises bring.
>I want to make stupid typo errors and have them appear as normal errors, not caught by promises.
For stupid typo errors use a linter and/or a transpiler like babel in the first place.
>It's cool, I just don't understand why everyone is so excited about a simple syntactic improvement.
Because better syntax brings code closer to how we think, and reduces errors.
In some way, promoting the use of "await" actually reduces the chance of an exception floating away into the ether. The "thread" joins back, both on success and on failure. And because promise errors are like exceptions, it's probably not a great idea to signal commonly-occurring conditions through them (instead using sentinel values like nulls, etc); so there isn't a huge pressure to try-catch every single thing.
Also, there is fundamentally nothing wrong with a promise error that never gets handled. It's an "if a tree falls in the woods" kind of thing, no? I will concur that it's not exactly beginner-friendly, though.
Promises and observables are related but operate differently, just as a single string relates to an array. Promises represent a single operation, there is nothing wrong with the abstraction itself.
As for the errors, I see what you mean. But at the same time this is an issue with trying to write async operations with sync constructs, something all "old" languages need to do. Async/await is a pattern that does that in languages that can't do it implicitly by requiring an explicit declaration. You don't need to write try/catch with async/await. You will get the exception propagated through the stack automatically.
«They run your code in a try/catch block and store the error away, and you have to remember yourself to manually throw it later.»
Browser native Promises and their corresponding Dev Tools work exactly how you would expect and surface uncaught exceptions in Promises. Browsers that support async/await will especially be incentivized to provide strong asynchronous exception handling in their Dev Tools.
It's just tough to polyfill that behavior without bringing the JS synchronous code world to a halt. A good Promises library/polyfill will at least attempt to console.log uncaught exceptions when they happen.
> Promises are not the only way to do async work in JS, as much as some people like to say that. You have observables and channels, which are more powerful because they handle streaming.
Let me just try and translate that into sync-land: "Single value variables are not the only way to do work in JS, as much as some people like to say that. You have arrays and queues, which are more powerful because they handle multiple values"
(my point being, different abstractions are useful for different things and/or in different ways)
> You shouldn't be forced to run code inside a try/catch.
How is throwing on typos useful? The real solution for that isn't letting errors get through. Its having a type system like TypeScript or Flow plus using a sensible library like bluebird that reports rather than swallows up unhandled errors by default. Afaik errors from native promises are reported to the console in most browsers. And there are also tools like this: https://github.com/emberjs/ember-inspector/wiki/Promises-Tab
Allowing thrown errors to propagate synchronously will destroy many contractual guarantees that make promises useful in nodejs. For example, we would have to remove this entire section in Bluebird: https://github.com/petkaantonov/bluebird/blob/master/API.md#... and instead fall back to process crashing and restarting. Lack of error catching is what makes node unable to provide a safe solution to the crashing problem (and domains capture errors in both directions of the call stack which leads to problems inside node internals). Promises neatly solve that by providing a sensible error capturing and propagation model that isolates node internals from promise code: https://github.com/petkaantonov/bluebird/issues/51
I'm confused, I don't know what type of errors a promise eats. If you're chaining together promises returned by other routines, especially with async/await, then error propagation and handling is very natural. If you're creating a promise using `new Promise` then you can use reject to propagate errors. How does this fall short of other async models?
The problem isn't the propagation, it's the propagation by default. He wants errors that occur in development to pop a stack trace as soon as the error occurs without needing to attach a .catch handler.
This requires the first method running in the stack to handle the failed promise and rethrow as an exception. It can be done today manually. But with async/await this will be done automatically.
I think you you (he?) needs RejectionEvent[0]. If that is so, it is a solved problem :)
Basically, implementations log the error to the console and raise the PotentiallyUnhandledRejection event if an error occurs inside a promise and no .catch handler was attached. If a .catch handler is attached _after_ the error is already logged, then a RejectionHandled event is raised. You can listen to those global events (the context is process in node, and window in browser, I think).
But... it's an async call. The error is going to happen as part of the event loop. That's inherent to the nature of async. If you want to get the error before it happens, you'll need a time machine.
About the points you make on your post, once async/await is implemented in browsers, you will be able to await run() directly, causing the exception to be thrown in the console.
The fact that you call a method that returns a promise but don't check this promise may be a little confusing to someone new to the language, but it it's not a flaw in the design.
In c#, any attemp to call a method that returns tasks (promise) without an await or a .continueWith (.then) causes a compiler warning. It's just a matter of time for tooling in the browser to keep up.
> About the points you make on your post, once async/await is implemented in browsers, you will be able to await run() directly, causing the exception to be thrown in the console.
I don't think you're right, unless TC39 has changed their minds. I argued hard for a top-level `await` but was strongly fought back.
If you can call into an async function in a way that immediately throws errors (that means that we can literally check the stack if the current async function should just throw the error), then I'm all good.
Indeed that might be the case. There are ES6 features implemented in chrome that don't work in the console (eg. classes). Still, it doesn't mean the language or the feature is doomed.
You have exactly the same issue with setTimeout. Errors will be thrown when the function runs, not when you schedule it. The problem you're describing is not an issue, and that's why today there are linters and compilers.
I just didn't want to get into the weeds about why his blog post is wrong here. In any case, his arguments against try...catch are silly. Syntax errors are not an argument against them, because you can easily detect them with other tools (and should be doing so). In practice, the only errors that will bubble up to your try...catch are ones about the async operation, not "typos".
So conceptually, async/await is just replacing the `await someMethod()` statement with the first argument to `resolve` or calling `catch()` with the first argument of `reject`?
// this
var x = await promise;
callSomething(x);
// translates as
promise.then(x => callSomething(x));
Under the hood, the entire "async" function is turned into a state machine instead of creating a lot of callbacks. It's more efficient it can do some optimizations that would be boring to do manually.
For example, if the translation were simply "turn into callbacks", this:
var p = getJson();
var x = await p;
var y = await p;
var z = await p;
would translate to something like :
var p = getJson();
p.then(o => { var x = o; });
p.then(o => { var y = o; });
p.then(o => { var z = o; });
but instead, the code runs more like this:
getJson().then(o => {
var x = o;
var y = o;
var z = o;
});
I say runs like because the compiled code is more complex than that, it will still call functions, etc. It will also handle exceptions, which would add a lot of boilerplate to this code. But have in mind that because this is generated by the compiler, this code will in most cases be more efficient than anything you try to write by hand.
Doing retries, or loops against promise actions are really cumbersome with promises directly, but with async/await becomes much more clear in terms of action and intent.
I'm not sure about the no promises and no callbacks part.
The similar line they use in their examples doesn't define a separate getJsonAsync function in the "Asynchronous city" section (and, as far as I can tell, there's no native getJSON function). That, and the fact that the line immediately preceding the await call has a comment that says, "just have to await the promise!", leads me to have to assume they used the function defined in the promises section, which of course uses promises. And, in order to define that promise, they do have to use three callbacks.
With functions and just callbacks I can make one-liners for fetching data too if I abstract away all the stuff that actually does the fetching of the data:
The parent exaggerated a little. async/await does use promises. In fact, it's built around them.
All async functions return promises, and all await expressions await on promises. Currently on babel implementation with facebooks's regenarator it seems to work on any "thenable", because I got it working with jquery and angular promises.
Promises are sufficiently supported in the latest versions of all major browsers, and Generators are almost there. They're supported in Firefox and Chrome, behind a flag in Edge, but unsupported in Safari.
But if you're targeting Node 4.0, you might as well transpile down to ES6 instead of going all the way to ES5 and pulling in Regenerator :)
When it's done, you will have the value in orders variable. If you can do something with it - just do it in new line in the same scope. You can also use catch exceptions using standard try catch block.
Also, if you're targeting a JS implementation with generators, but no await, Q has an async method that uses generators to achieve the same result, albeit with "yield" instead of "await" (in fact, I believe babel's experimental await support uses generators as an intermediate transform; sure looks like it from the code it generates).
This means you can get await-like promise handling in Node.js using the --harmony_generators flag.
If you're writing a library, use the runtime option. If you're writing a website or other standalone app, you should use the polyfill (especially if you use other libraries like React which depend on ES6 globals too).
Firefox has had this for over 6 years through my async.js[1] library that relied in JavaScript 1.7's pythonic iterators and generators. The syntax is similar, as you have to mark/encapsulate functions with async() to use the library.
Don't you mean the other way around? Mine was out years before theirs. They should have googled the words "async.js" (which my lib was the top result for prior to caolan's repo) before naming their project.
Years? Really? Not to nit, but the only way "years" is accurate here is if you mean "0.3 years".
Your first commit was that January and Caolan's was that May, and when yours was "out" is debatable anyway since you don't have any releases tagged.
I would hardly feel obligated to rename a project over a 4-month-old Github repository that doesn't even target the same platform (Firefox vs. async being originally Node-only).
Sephr's eligrey/async.js predates (01/2010) the lib you mention, caolan/async (05/2010), at least git history–wise, although the latter looks actively developed.
Which argues that async/await might not be what we really want in the end. As a user of Promises and async/await in the future, it's a very interesting read!
It's a great blog post, but be careful what you wish for. If you have true preemptive threading as in Java or Go, a race condition can happen anywhere. I suspect many JavaScript developers would be sad about not having atomic event handling by default and having to learn to code much more carefully.
Even in Java UI frameworks like on Android or Swing, you have an event loop with a single thread, and when spawning another thread you have to deal with special communication layers [1]. True multithreaded UI's seem like an area for research.
Yeah, the event loop can be a constraint but it's easier to think about and harder to mess up compared threads or channels. And really how much are you giving up? Most of the things you actually want to wait on have an async api. Maybe I just haven't built complex enough things, but I've never had to reach for web workers because of performance concerns.
I feel like Microsoft doing this is their new attempt at trying to dominate how the web will be shaped. I know the likelihood of async/await being bundled with ES7/ES2016 is pretty high but lets say it gets delayed for further review, is Microsoft going to wait until these specs are "official" before releasing it into standard non-preview Edge or are they just going to rush it as fast as possible, pat themselves on the back and then shrug their shoulders when their syntax is completely different from the actual ES7 implementation?
async/await is important and highly desired by the community, but I would prefer if it were not Microsoft trying to blast ahead and create yet another separate fragmentation between browsers. Sure they call it an experimental feature, but how long until they decide, "well we're not going to wait for this to get finalized, we're releasing it for everyone!!!" ?
To be fair, all browsers are still missing ES6 features and all of them have newer stuff being implemented.
As for the try catch, you don't NEED to wrap. The sample just shows you can. You can't do:
try {
promise.then(() => ...);
} catch {}
but you can do
try {
await promise;
...
} catch {}
async/await is a way to write simpler code the same way you would use a "for" block instead of a "while(iterator.next())" - because it's easier, all the rest is still the same.
You need to wrap in try/catch if you want to handle the errors, which is the same for sync functions that throw to be fair. But most of async operations are somehow expected to fail due to their nature (like network calls) and people usually want to have error handling scenarios. But for sync functions we usually let the error terminate the program because if a sync function throws in runtime it means something is wrong in my program, not in networking...
> You need to wrap in try/catch if you want to handle the errors.
Of course, the same way you need to add a second callback to a promise handler if you want to handle errors. There is no difference, really. You're just changing the way you write the code.
Also, you're not forced to choose "await or promises". You can use both as you see fit, in the same block:
async () => {
// make 2 requests in parallel
var p1 = fetchSomething();
var p2 = fetchAnotherThing();
// wait for both of them to be done
await Promise.all(p1, p2);
// continue when they're both resolved
// these "await" only get the values since the promises are resolved
doSomethingWithTheValues(await p1, await p2);
}
> But for sync functions we usually let the error terminate the program because if a sync function throws in runtime it means something is wrong in my program, not in networking
This is just the way you're choosing to see the problem. Promises can be used for anything, it's not tied to networking. I usually write promises that will be resolved when a modal window closes or when an application event happens.
It's just a simpler way to write code. The way you use it is up to you.
Also, async/await makes debugging a lot easier. The debugger can understand that the next expression after an "await" is supposed to be in the same function context. No more adding breakpoints to the callback function because only you know how the code is called at runtime. You debug async functions as normal functions.
I think you meant the above example and not the one you listed in the comment as generator objects in JS always return objects upon calling next method even after finish yielding for long and thus you could have an infinite loop on your hand here.
FunScript is a lightweight F# library that lets you rapidly develop single-page applications. You can connect to external data sources and call REST APIs with intellisense, produce dashboards using JavaScript visualization libraries and write asynchronous computations easily without explicit callbacks."
I'd be curious about one potential downside to this (v8 specific, maybe others too)
Functions that contain a try/catch cannot currently be optimized by V8. If we migrate to using try/catch rather than `.then/.catch`, I wonder if there's going to be any perf implications since previously optimizable functions may no longer be optimized.
"await" will not help you if you do not understand the asynchronous nature. I think it's a unnecessary abstraction. And it's much easier to follow callbacks if you abstract them like this:
socket.on("data", echo); // calls the echo function when data arrives
O the tone of MS harping on about what Edge is doing feels like smoke and mirrors to me whilst IE11 will remain one of our lowest common denominators for some time. I respect that a team would love to forget about IE11, but MS is the only company that has any control over it... so whilst they dazzle people with c# style syntactical sugar and pushing typescript (which we will have to compile down to ES5 anyway) I find it hard to get excited about Edge.
Get the basics done right first imvho. I don't want developers staying late to try and understand why something isn't working in a MS browser anymore.
Edge is not cross platform so those of us who don't have any devices running windows don't have any exposure to it in order to praise it. That said, nobody I know refers to Edge as "just IE"; in fact, it's not even considered IE (the name is Microsoft Edge, right?). In any case it's good news that Windows users will finally have decent browser by default. That is, when Edge adoption/usage is higher than all IE versions combined).
My point being: IE is just IE. Edge is Edge and we're happy about it.
Just being picky, but because Edge was released after all other browsers, technically there isn't any other "more modern browser". There are just the other browsers.
Do we have an ES7 compiler to ES6 that can handle these new features? Whilst I prefer cljs, sometimes the weight of clojurescript output and tooling can be a little offputting for smaller tasks, but callbacks/promises etc.. are rarely enjoyed
...which is cool, because using async/await in C# made me realize how much simpler dealing with async code could be (compared to my experience with JS callbacks or Java threads).