I want to use an async function as a default parameter but I get an error: 'await' is not a valid idientifier name in an async function. Is this a language limitation or have I missed something?
Background
There is support for using functions to default parameters in a pretty neat way:
> a = ({ no }, b = (function(c) { return c -1; })(no)) => console.log(no,b);
[Function: a]
> a({ no: 2 })
2 1
This could allow for passing results from expensive database calls while retaining the option of getting the parameters if null. This requires though that we can do an await inside the parameter call:
> a = async ({ no }, b = await (async function(c) { return c -1; })(no)) => console.log(no,b);
a = async ({ no }, b = await (async function(c) { return c -1; })(no)) => console.log(no,b);
^^^^^
SyntaxError: 'await' is not a valid identifier name in an async function
I suppose that this is a feature from async/await being syntactic sugar for Promises and rewriting the above as a Promise would be rather challenging. Still, the pattern would be really neat and If I've missed something, please enlighten me.
Thanks!
It's a limitation, specifically in this case AwaitExpression is not allowed in CoverCallExpressionAndAsyncArrowHead (which includes the formal parameters). The second bullet point is:
It is a Syntax Error if CoverCallExpressionAndAsyncArrowHead Contains AwaitExpression is true.
...but the same language is present for the various other function definitions.
I probably don't have to tell you that you can work around this by just not awaiting the async function until within the body:
const a = async ({ no }, b = (async function(c) { return c -1; })(no)) => {
// Removed await ------------^
b = await b; // <== Then added it back within the function body
console.log(no,b);
};
a({no: 3});
Using an await expression in a default parameter is a fascinating thought. I suspect the reason it's not (yet?) allowed is primarily complexity management. (This is speculation on my part, take it with a grain of salt.) The beginning of an async function is synchronous (that's how you start your asynchronous operation, after all), it only becomes async as of the first await or return (or implicit return at the point of falling off the end of the function). Having it become async prior to the beginning of the function body adds a fair bit of complexity. Not that it couldn't be done. :-)
However, looking through the closed issues on the proposal, an issue was raised asking what happens when a default parameter initialization throws an exception, should it throw or return a rejected promise? Which led to a TC39 discussion in which it was determined that it should return a rejected promise, thus this issue, the resolution of which moved evaluation of the parameter initialization into the function body (so exceptions could be part of the promise). Which would seem, to my mind, to open up the possibility of allowing await in the formal parameters of an async function...
Related
I am new to asynchronous code in general (and JavaScript in particular) and have been falling prey to common pitfalls, so I want to re-evaluate my most basic assumptions.
Testing the code below does not give me an answer since, just because the test passed, doesn't mean the code will run the same way in the future.
The question is very general, so I will give a couple concrete examples of the basics I am trying to understand.
First Example
const secret_key = "BLAH BLAH";
var url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
If the above code occurs in an async function, can I be sure that url will be defined correctly (i.e. that the second statement will occur after the first)? Does this change if secret_key is a var? Or let?
Second Example
blah = async function() {
syncFunction();
}
Can I be certain that the synchronous function will be run to completion before the function returns (even if it has to do some time-intensive work)?
I ask because my understanding is that this would not be true for an asynchronous function (as explained here) unless I use await / promises.
Update
In the end, this Stack Overflow question resolved my problem. Basically I was using await, but the function being called had an undefined Promise.
First example
can I be sure that url will be defined correctly (i.e. that the second
statement will occur after the first)?
Why yes, absolutely. These are just two regular lines of codes, you simply store two strings in two variables; of course they will be executed in order, there's nothing asynchronous here.
Second example
Using Typescript instead of Javascript helped me a lot to understand how async functions work. Here's your example in Typescript :
const syncFunction = ():string => {
return "hello"
}
const blah = async ():Promise<string> => {
return syncFunction()
}
(async () => {
const sayHello:string = await blah();
console.log(sayHello) // "hello"
})()
As you can see, adding the types helps.
An async function always returns a Promise. If you return true, then the returned type isn't boolean, it is Promise<boolean>.
So, return syncFunction() returns a Promise of "hello". Therefore, you must await it to actually get your "hello" string. Then you can log it.
Same code transpiled to JS so it is runnable in a snippet :
const syncFunction = () => {
return "hello"
}
const blah = async () => {
return syncFunction()
}
(async () => {
const sayHello = await blah();
console.log(sayHello) // "hello"
})()
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.
As far as I can tell, both the spec and the documentation have await as the only reserved keyword out of the async/await feature.
This is further demonstrated by the fact that we can name a variable async:
For example:
var async = 5;
console.log(async) // this is fine
Node (6.10) (also on Repl.it)
Chrome (59)
Firefox (54)
Is it because of backwards compatibility? I'd guess many codebases would use the name async for certain features.
This allows for some strange looking code examples:
async function async() {
var async = 5;
await async;
return async;
}
async().then(console.log)
Infinite recursive promise chain? (Not really important since any function name would allow this, however this code looks additionally confusing)
async function async() {
await async();
}
// stackoverflow (might need to open your console to see the output)
async does not need to be a reserved word, because it can be uniquely identified. The contexts in which it can occur are those such as
async function() { }
async () => { }
obj = { async foo() { } };
All of these could not be parsed in any way other than seeing async as indicating an async function.
On the other hand, await can in theory be used in a statement such as
async function foo() {
await(1);
}
which is ambiguous; is await awaiting the value 1, or is it a function being called with the parameter 1? Therefore, await needs to be a reserved word (inside async functions; outside, feel free to use it as a variable).
Remember that JavaScript has evolved greatly since its origin. Many words were designated as reserved, and then never used, or designated as reserved when technically they might not need to have been. The designation of await as a reserved word (within modules), and the non-designation of async as a reserved word, are the product of a more mature understanding of the language on the part of its designers.
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.
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.