I am confused about the current discussion of adding async functions and the keyword await to the next EcmaScript.
I do not understand why it is necessary to have the async keyword before the function keyword.
From my point of view the await keyword to wait for a result of a generator or promise done, a function's return should be enough.
await should simple be usable within normal functions and generator functions with no additional async marker.
And if I need to create a function what should be usable as an result for an await, I simply use a promise.
My reason for asking is this good explanation, where the following example comes from:
async function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// some more logic
}
It also could be done as normal function, if the execution of a function will wait for finishing the hole function until all awaits are fulfilled.
function setupNewUser(name) {
var invitations,
newUser = await createUser(name),
friends = await getFacebookFriends(name);
if (friends) {
invitations = await inviteFacebookFriends(friends);
}
// return because createUser() and getFacebookFriends() and maybe inviteFacebookFriends() finished their awaited result.
}
In my opinion the whole function execution is holding until the next tick (await fulfillment) is done. The difference to Generator-Function is that the next() is triggering and changing the object's value and done field. A function instead will simple give back the result when it is done and the trigger is a function internal trigger like a while-loop.
I do not understand why it is necessary to have the async keyword before the function keyword.
For the same reason that we have the * symbol before generator functions: They mark the function as extraordinary. They are quite similar in that regard - they add a visual marker that the body of this function does not run to completion by itself, but can be interleaved arbitrarily with other code.
The * denotes a generator function, which will always return a generator that can be advanced (and stopped) from outside by consuming it similar to an iterator.
The async denotes an asynchronous function, which will always return a promise that depends on other promises and whose execution is concurrent to other asynchronous operations (and might be cancelled from outside).
It's true that the keyword is not strictly necessary and the kind of the function could be determined by whether the respective keywords (yield(*)/await) appear in its body, but that would lead to less maintainable code:
less comprehensible, because you need to scan the whole body to determine the kind
more errorprone, because it's easy to break a function by adding/removing those keywords without getting a syntax error
a normal function, whose execution will wait for finishing the hole body until all awaits are fulfilled
That sounds like you want a blocking function, which is a very bad idea in a concurrent setting.
By marking a function as async, you're telling JS to always return a Promise.
Because it will always return a Promise, it can also await on promises inside of its own block. Imagine it like one giant Promise chain - what happens internally to the function gets effectively gets bolted on to its internal .then() block, and what's returned is the final .then() in the chain.
For example, this function...
async function test() {
return 'hello world';
}
... returns a Promise. So you can execute it like one, .then() and all.
test().then(message => {
// message = 'hello world'
});
So...
async function test() {
const user = await getUser();
const report = await user.getReport();
report.read = true
return report;
}
Is roughly analogous to...
function test() {
return getUser().then(function (user) {
return user.getReport().then(function (report) {
report.read = true;
return report;
});
});
}
In both cases, the callback passed to test().then() will receive report as its first parameter.
Generators (i.e. marking a function * and using the yield keyword) are a different concept altogether. They don't use Promises. They effectively allow you to 'jump' between different portions of your code, yielding a result from inside of a function and then jumping back to that point and resuming for the next yield block.
Although they feel somewhat similar (i.e. 'halting' execution until something happens somewhere else), async/await only gives you that illusion because it messes with the internal ordering of Promise execution. It's not actually waiting - it's just shuffling when the callbacks happen.
Generators, by contrast, are implemented differently so that the generator can maintain state and be iterated over. Again, nothing to do with Promises.
The line is further blurred because at the current time of writing, support for async/await is scare; Chakracore supports it natively, and V8 has it coming soon. In the meantime, transpilers like Babel allow you to write async/await and convert the code to generators. It's a mistake to conclude that generators and async/await are therefore the same; they're not... it just so happens that you can bastardize how yield works alongside Promises to get a similar result.
Update: November 2017
Node LTS now has native async/await support, so you ought never to need to use generators to simulate Promises.
These answers all give valid arguments for why the async keyword is a good thing, but none of them actually mentions the real reason why it had to be added to the spec.
The reason is that this was a valid JS pre-ES7
function await(x) {
return 'awaiting ' + x
}
function foo() {
return(await(42))
}
According to your logic, would foo() return Promise{42} or "awaiting 42"? (returning a Promise would break backward compatibility)
So the answer is: await is a regular identifier and it's only treated as a keyword inside async functions, so they have to be marked in some way.
The reason for the async keyword in front is simple so you know that return value will be transformed into a promise. If no keyword how would the Interpreter know to do this.
I think this was first introduce in C# and EcmaScript is taking a loot of stuff from TypeScript. TypeScript and C# are conceived by Anders Hejlsberg and are similar.
lets say you have a function (this one is just to have some asynchronous work)
function timeoutPromise() {
return (new Promise(function(resolve, reject) {
var random = Math.random()*1000;
setTimeout(
function() {
resolve(random);
}, random);
}));
}
this function will make us wait for a random time and return a Promise (if you use jQuery Promise is similar to Deferred) object. To use this function today you would write something like this
function test(){
timeoutPromise().then(function(waited){
console.log('I waited' + waited);
});
}
And this is fine. Now lets try to return the log message
function test(){
return timeoutPromise().then(function(waited){
var message = 'I waited' + waited;
console.log(message);
return message; //this is where jQuery Deferred is different then a Promise and better in my opinion
});
}
Ok this is not bad but there are two return statements and a function in the code.
Now with async this will look like this
async function test(){
var message = 'I waited' + (await timeoutPromise());
console.log(message);
return message;
}
The code is short and inline. If you written a lot of .then() or . done() you know how unreadable the code can get.
Now why the async keyword in front of function. Well this is to indicate that your return value is not what is returned. In theory you could write this(This can be done in c# I don't know if js will allow since it's not done).
async function test(wait){
if(wait == true){
return await timeoutPromise();
}
return 5;
}
you see, you return a number but the actual return will be a Promise you don't have to use
return new Promise(function(resolve, reject) { resolve(5);};
Since you can't await a number only a Promise await test(false) will throw an exception and await test(true) will not if you don't indicate async in front.
Related
I've researched many many posts and articles, and documentation. I'm seeking deeper understanding.
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
The await expression causes async function execution to pause until a Promise is settled, [...]
let fetchLocation = () => {
//fetches information from satellite system and
//returns a Vector2 of lat long on earth
return new Promise(resolve => {
setTimeout(() => {
resolve({
lat: 73,
long: -24
});
}, 2000);
});
}
//async functions automatically return a promise.
let main = async() => {
//awaits the promises resolution
let result = await fetchLocation();
console.log("I'm the awaited promise result", result)
//immediately returns a promise as promise pending
return result;
}
console.log(main().then(res => console.log("I'm the original promise, just passed along by the async function, it didn 't a-wait for me", res)))
Before going down this rabbit hole, I knew async/await was syntactical sugar to make async code look synchronous.
I therefore fully expected to see main return the lat long coordinates. Instead it returns a promise.
A couple questions.
Is the MDN documentation wrong? Does it not actually pause execution? It seems that the await keyword does not actually "pause" function execution... invoking main immediately returns a Promise<pending> object. However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop? (And an async return statement is still synchronous.)
So it seems that await unwraps a promise, and async wraps a promise, but you need to use await within async. I get that await might actually be promise.then() underneath the hood but if they want to make it fully synchronous in appearance, why not have the async function return the resolved promise value when used with await? Otherwise async await seems a bit redundant.
Is the MDN documentation wrong?
No
Does it not actually pause execution?
It pauses the execution of the async function, hands a Promise back to the calling function — because it hasn't reached the point in the async function where it knows what to return — then the execution of the calling function (and the rest of the code) continues.
However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop?
When the event loop stops being busy (running the code that called the async function in the first place and anything else that it picks up in the meantime) and the promise that was being awaited resolves, then the async function is woken up and given the resolved value of the function to continue processing.
why not have the async function return the resolved promise value when used with await?
Because that would block the entire event loop, which is why asynchronous logic (starting with callback style APIs) was introduced into JS in the first place.
Otherwise async await seems a bit redundant.
Consider a situation where you want to request a number of URLs from an API serially (so that you don't shove too many simultaneous requests at the API):
const data = [];
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const response_data = await response.json();
data.push(response_data);
}
If you were to rewrite that without await then you'd probably end up with something recursive to count your way through the loop. It would be significantly more complex.
async/await makes dealing with complex combinations of promises simple, and simple promises trivial.
knew async/await was syntactical sugar to make async code look synchronous.
And it does that inside the async function and only inside it.
It doesn't really pause the function, it just allows you to write the rest of the function code as if it were. But it's really just wrapping the rest of the code of the function into a .then().
E.g.
await foo = someFunc();
console.log(foo);
is executed like
someFunc().then((foo => {
console.log(foo);
});
Since it's really still asynchronous, you can't return the resolved value, because the original calling function returns immediately. But if the calling function is also declared async, it returns a Promise, and the caller can either use await again (so it appears to be synchronous) or use .then().
This question already has answers here:
Why do I have to put async keyword to functions which have await keywords?
(1 answer)
JS async/await - why does await need async?
(3 answers)
Why 'await' requires 'async' in function definition
(4 answers)
Nodejs why is await only restricted to async functions?
(3 answers)
ES2017 - Async vs. Yield
(4 answers)
Closed 2 years ago.
One has to use async keyword on the containing function to use the await inside the function body.
async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
fetchMovies();
The await is being used to block on completion of the asynchronous fetch() call. As can be seen in the code, the function fetchMovies() is not even returning any value. And even if it did, it would affect the way the return value is consumed by the caller, but why should it matter to the call to another asynchronous call from the function body?
My question is why is this required? Is there some good explaination of that? Is it related to the need for actual implementation of await and supporting it in older JavaScript versions?
I know iffi pattern is used be able to use await but does that change the semantics for the code that follows the iffi code block in any way?
(async () => {
const response = await fetch('/movies');
console.log(response);
})();
I am also aware of top level await being supported in modules.
May be I am missing something really obvious.
There are three reasons the async keyword exists:
In ECMAScript language versions prior to 2015, await was not a keyword. Marking a function async provides a syntactic "bailout" to indicate a breaking change in the language grammar within the body of the function.
This is the most important reason. Without the async keyword, all programs written in ECMAScript 5 or older would no longer work if they used the await keyword as a variable (in fact this was done intentionally in some cases as a polyfill before async/await was standardized), since that would cause a breaking change without the addition of async to the specification. Because of this, async is syntactically necessary to avoid breaking changes to the language.
It provides a convenient marker for parsers, avoiding an infinite look-ahead in order to determine whether or not a function is asynchronous.
This makes parsing more efficient, which is appealing for both ECMAScript implementers and developers, though this reason alone does not make async strictly necessary to the syntax.
async also performs its own transformation on the function, which is done regardless of whether or not the await keyword is present in the body.
Consider the following two functions:
function foo() {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
}
async function bar() {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
}
async performs the following transformation of function bar():
function bar() {
return new Promise((resolve, reject) => {
try {
resolve((/*async function bar*/() => {
if (Math.random() < 0.5) {
return 'return';
} else {
throw 'throw';
}
})());
} catch (reason) {
reject(reason);
}
});
}
Those familiar with promises will recognize that we can simplify the above since the Promise constructor executor function will implicitly reject if it throws an error synchronously:
function bar() {
return new Promise((resolve) => {
if (Math.random() < 0.5) {
return resolve('return');
} else {
throw 'throw';
}
});
}
There are two questions being asked here. Why do you need the async keyword to indicate an async context, and why do you need async context to use await?
If we didn't use the async keyword to indicate an async context, then we would have backwards compatibility issues. Before the async/await update, "await" was a valid variable name. So to make it a reserved word, we need to introduce a new context, the async function context.
The second question is, why do we want this in the first place? Why do we want to differentiate async functions from traditional synchronous code? As the name implies, async functions do not execute all their code at once. When a function "awaits", it effectively stops execution, and the remainder of the code becomes an event handler for the thing being awaited. This allows other code to run.
Browser javascript implementations are single-threaded. Only one thing can be performed at a time, but the event queue allows functions to take turns. Consider what would happen if you could await from a synchronous function. The synchronous code, by definition, does not give up control. Therefore, the async function it's waiting for will never get a chance to swap in to your single execution thread, and you will be waiting forever. So, you can only await an async function if you are already in an async context.
I assume your exact question is this: "Handling return values (null or something) depends on the consumer who called the function. They "should" supposedly get it even if another asynchronous function is called in-between. So why does it matter to wait before further other asynchronous calls?"
You see, such fetch() calls are done in Databases within the duration of "initiating" and "closing" the connection. Almost every method used is asynchronous in this case. So while you're executing fetchMovies(); The thread might move further and execute connection.close(); before the fetching is resolved and returned.
The exact scenarios are like this:
await client.connect(); //connection establishment
// Your purposeful methods
async function fetchMovies() {
const response = await fetch('/movies');
console.log(response);
}
await fetchMovies();
// Closing connection to avoid attacks and maintain concurrency
await client.close();
If any method, in this case, is called in an asynchronous manner, the whole session of a Database connection is wasted and our function would return undefined or throw an error "Cannot use a session that has ended"
So we need to "wait" for the "Pending" Promises to reach a "Fulfilled" or "Rejected" state before executing further calls.
You can refer more to this article: Using Promises, async / await with MongoDB just for the sake of understanding.
I think it's to make it clear that the function contains asynchronous code. Let's use another example that does return something:
async function canUseGeolocation() {
const result = await navigator.permissions.query({name: 'geolocation'});
return result.state;
}
The async keyword tells the javascript engine that the function should return a promise, and any return statements in the function should resolve that promise. What happens if we modify it to cache the values so we don't always call the await?
function canUseGeolocation() {
if (cachedPermissionState) return cachedPermissionState;
const result = await navigator.permissions.query({name: 'geolocation'});
cachedPermissionState = result.state;
return result.state;
}
How should javascript know that the function should return a promise? Because it contains an await? What if you later change the function so that the cachedPermissionState is set elsewhere and this function only returns that value so you remove the await? Should that first return statement return a promise or return the value? That would now change the whole way the function is executed and what is returned by return cachedPermissionState;. By using async, we can know that it really returns a promise that the return statement resolves without scanning the function for await statements to determine if it should be treated as async or not.
This question already has answers here:
Difference between `return await promise` and `return promise`
(7 answers)
Closed 4 years ago.
For the most part, I think I understand how async functions work in JavaScript / Node.js, and I am familiar with the syntax of async/await. But this question is about a very specific case, which I can’t seem to find anywhere, and I think it involves the inner workings of await, which is beyond my understanding.
First I’ll start off with a simple example, and then I’ll extend it to real working code. (Note: I’m actually using Typescript, so you’ll see void instead of undefined, but that’s not important to the problem.)
async function subtask() {
// doSomeStuff();
return; // returns a `Promise<void>`
}
// async function run() {
// see options below
// }
function app() {
run().catch((e) => console.error(e));
}
Option 1
async function run() {
await subtask(); // resolves to `void`
return; // returns a new `Promise<void>`
}
Option 2
async function run() {
return subtask(); // returns a new Promise, resolving to the `Promise<void>` of `subtask()`
}
In the simple example above, I have an async function run that calls a smaller async function subtask. Both functions must return a Promise<void>. I have two options: (1) to await the smaller function and return a new Promise<void>, or (2) to return a wrapped promise, given by the smaller function, which will later resolve to void.
My lack of understanding is about how this works. In Option 1, is execution is paused before subtask() returns? What does that actually mean? Does that mean that the async subtask executes synchronously? Does that mean that app, which calls run(), will also pause execution? What if app was async, would that make a difference?
Is it “better” (more performant) to let the promise bubble up and resolve later, or to resolve it directly within the run function?
The reason this is important is that in my real code I have a bunch of smaller subtasks, which all return void, and then the big function must also return void—it cannot return an array. (Note that the subtasks need not run in any particular order.)
Option 1
async function run() {
await Promise.all([
subtask0(),
subtask1(),
subtask2(),
]);
return;
}
Option 2
async function run() {
return Promise.all([
subtask0(),
subtask1(),
subtask2(),
]).then((_all) => {});
}
function app() {
// do some stuff
run(); // if `run` contains `await`, does execution pause here?
// what if `app` was async?
// do some more stuff
}
It is always better to let the Promise bubble up. This avoids the creation of an extra Promise object that will also be awaited on (whether this is optimized away behind the scenes in whichever JavaScript engine you're using is up to debate, though).
async function run() {
await subtask();
return;
}
This creates an extra Promise (and subsequently an extra callback in the chain that will be executed).
async function run() {
return subtask();
}
This isn't actually doing what you think it is. This is also creating an extra Promise (since you're using the async keyword) and is almost functionally the same as the previous example. By using the async keyword, you're creating and returning a new Promise that will resolve/reject to the same value as the Promise created from the call to subtask(). If you remove the async keyword, then this will avoid the creation of the extra unnecessary Promise .
Now with your Promise.all() examples, I think they're both optimal (assuming you remove the unnecessary async keyword from the 2nd one, like noted above). The first would create 2 Promise s (the Promise.all() one, and the one created from returning from an async function), and the 2nd would as well (the Promise.all() one, and the one created from calling then()). Whichever you'd want to use is basically up to personal choice. Personally, I like the 2nd example since it's not mixing the use of async functions and Promises, which I think makes it a little easier to understand.
And for the last part of your question, execution will be paused wherever the await keyword exists (so within the run() call).
You can also essentially think of the await keyword transforming your code from this:
// Do some stuff
let result = await run();
// Do some other stuff
to this:
// Do some stuff
run().then(result => {
// Do some other stuff
};
(the whole point of await is to reduce confusion/complexity caused by nesting)
I stumbled upon the following ES6 method during a rewiew:
async getUsers(res) {
User.findAll().then(users => res.json(users));
}
This seems buggy to me. I would have expected the following:
async getUsers(res) {
return User.findAll().then(users => res.json(users));
}
But then, I know that async has many tricks such as auto-wrapping promises, so is my intuition correct?
The code as it is written (without the return) is likely fine as long as the caller does not need access to the asynchronously retrieved value.
First off, async is needed as part of your function definition ONLY if you plan on using await inside the function. If you aren't using await inside (which your code does not show), then the async part of the definition is not needed. So, it could probably be just this:
getUsers(res) {
User.findAll().then(users => res.json(users));
}
Second, if a caller wants to use await on the getUsers() function, then it needs to return a promise (as you proposed). If the caller will not use await with it or otherwise need any access to a return value, then the return is not necessary. So, if you wanted to use await on this function, then it should probably be this:
getUsers(res) {
// return promise so caller can get async value
return User.findAll().then(users => {
res.json(users);
return users; // return value so caller can get access to the value
});
}
So, the original code may be just fine as long as the caller is never expecting an asynchronous return value from that function (which is possible here because the result of the async operation is being sent with res.json() and that may be the only result that is needed. As far as await/async are concerned, you only have to return a promise from functions that you plan to use await on.
To recap:
async is only needed if you plan on using await inside the function. Your function probably doesn't even need the async keyword on it at all.
A returned promise is only needed if the caller wants to be able to get access to the asynchronous response either via a traditional f().then() or via let x = await f().
A caller can await a function that is not marked as async. The await will get the return value from the function and, if the return value is not a promise it will wrap it in a resolved promise. If it is a promise, it will wait for it to resolve.
FYI, this is a very good, short and sweet review of async/await and how they interact with promises.
I'm trying to understand better what an async function in JavaScript is technically, even if I basically know how to use them.
Many introductions to async/await make belive that an async function is basically just a promise, but that obviously is not the case (at least not with Babel6-transpiled code):
async function asyncFunc() {
// nop
}
var fooPromise = new Promise(r => setTimeout(r, 1));
console.clear();
console.log("typeof asyncFunc is", typeof asyncFunc); // function
console.log("typeof asyncFunc.next is", typeof asyncFunc.next); // undefined
console.log("typeof asyncFunc.then is", typeof asyncFunc.then); // undefined
console.log("typeof fooPromise is", typeof fooPromise); // object
console.log("typeof fooPromise.next is", typeof fooPromise.next); // undefined
console.log("typeof fooPromise.then is", typeof fooPromise.then); // function
Still, it is definitely possible to await a promise, like await fooPromise().
Is a async funtion a thing of it's own and await is simply compatible with promises?
and, is there a way to distinguish between a simple function and an async function at runtime (in a Babel-compatible way)?
An async function is a function that returns a promise. It helps you with cases where you have a bunch of asynchronous actions happening one after the other:
function asyncFunc() {
return doSomethingAsync() // doSomethingAsync() returns a promise
.then(() => {
// do some stuff
return doSomethingElseAsync(); // returns a promise
})
.then(something => {
// do some stuff
return doSomethingElseEntirelyAsync(something); // returns a promise
});
}
Turns to
async function asyncFunc() {
await doSomethingAsync(); // awaits for a promise
// do some stuff
let something = await doSomethingElseAsync(); // awaits for a promise
// do some stuff
return doSomethingElseEntirelyAsync(something); // returns the final promise
// Note that even if you return a value, like return 5, the function as a whole
// still returns a promise!
}
It reads a lot better, and you can use the normal tools like try/catch and for loops to work with them, even though they're async.
Async functions are NOT a replacement for promises, they're sugar on top of them, to handle specific cases where you have many sequential asynchronous actions.
Because await is basically just "await for this promise", you can still use the cool aggregation methods like Promise.all() and Promise.race() and await for the result of several (or the first of several) promises.
I'm not familiar with a way of distinguishing the two in runtime because, like classes, async functions are just sugar on top of Promises. (Although there might be hacks like using the function's .toString and parse the results, I don't count those).
The couple async/await are a mechanism that let you write async code in an synchronous style and in my humble opinion it is the most simple and readable syntax so far to deal with asynchronous code (see also this article). The power of the syntax is really in how the await works. But in order to use await inside the body of a function, the function must have to be prefixed with async.
If you need more information there is a spec for async/await here.
The current implementation in Babel 5 is based on https://github.com/facebook/regenerator. As you can see in the transpiled code the function is compiled to:
function asyncFunc(which, one, two) {
return regeneratorRuntime.async(function asyncFuncMaybe$(context$1$0) {
...
If you dig in Babel's babel-regenerator-runtime package you find Facebook's code. At line 205 you find:
// Note that simple async functions are implemented on top of
// AsyncIterator objects; they just return a Promise for the value of
// the final result produced by the iterator.
runtime.async = function(innerFn, outerFn, self, tryLocsList) {
...
In order to transpile to ES5 the async/await Babel needs do to rearrange the code so we can keep track where we are during the execution of the function and the AsyncIterator is the object that keep track of that state.
Babel 6 gives you more options and let you choose the implementation that you want to use. See Transpile Async Await proposal with Babel.js?
So regarding your questions:
async/await are both a thing of it's own. According to the spec, they must work with promises. In particular you can await on a promise and when you execute an async function, it will return you a promise.
Since an async function is transpiled to function that return a promise, there is not a straightforward way to distinguish it from a no-async function that return a promise.
Your fooPromise should look more like var fooPromiseFunc = function() {return new Promise(r => setTimeout(r, 1))}; that make fooPromiseFunc and asyncFunc indistinguishable from a black box prospective. They are both functions that return a promise. What is the reason why you want to distinguish an async and no-async function at runtime? In practice they can be usedin the same way, so I do not see why you will have to threat them differently. For debug purposes if you really need to find out if a function is defined async, in Babel 5 you could use something like (asyncFunc+"").indexOf('regeneratorRuntime.async') >
0 or a more accurate regular expression. But that is really
hacky and I would not use in a context outside of debugging or study.