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.
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.
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.
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.
There are proposals to make async/await more composable: https://github.com/jhusain/compositional-functions. TC39 is aware of this but I'm not sure if they are going to fix it or not.
> 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
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.
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.
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.
The compiler probably does that for you, honoring catch blocks. It does in C#, I see no reason why it wouldn't in ES7.
So, async/await is actually less likely to eat exceptions.
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)
> There are proposals to make async/await more composable: https://github.com/jhusain/compositional-functions. TC39 is aware of this but I'm not sure if they are going to fix it or not.
I agree, but I don't think we need async/await at all. http://spion.github.io/posts/es7-async-await-step-in-the-wro...
> 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
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.
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).
0: https://github.com/domenic/unhandled-rejections-browser-spec...
try {
await do_thing();
} catch {
// works
}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.
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.
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.
// 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.So conceptually, it's what I described; but in practice, it's optimized?
while (somecondition) {
try {
await sleep(1000);
somecondition = await someAsyncAction();
} catch(err) {
}
}
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. function iff(condition, method) {
return Promise.try(condition).then(result => {
if (result) {
return method();
}
});
}
function whilst(condition, method) {
return iff(condition, () => {
return Promise.try(method)
.then(whilst.bind(null, condition, method));
});
}
whilst(someAsyncAction, Promise.sleep.bind(null, 1000))
.catch(err => {
// ...
})
Naturally you wouldn't want to re-implement all of this control flow every time you need it. But the end result is not too much boilerplate.