I noticed that the async
keyword can be assigned any value whatsoever and even used as a normal variable:
let async = "world";
console.log(async)
console.log("Hello " + async)
Yet, even then it continues to operate as before:
let async = "world";
async function foo(input) {
return input;
}
let barPromise = foo("bar");
console.log("bar promise is:", typeof barPromise);
console.log("bar promise also has a .then:", typeof barPromise.then === "function");
barPromise
.then(output => console.log("bar promise returns:", output));
console.log("async is still assigned:", async);
foo("Hello ")
.then(output => console.log(output + async));
Even await
is doing the same thing:
let await = "world";
console.log("await is:", await)
async function foo(input) {
return input;
}
async function bar(input) {
console.log("before");
let output = await foo(input);
console.log(output);
console.log("after");
}
bar("hello")
What is going on here? Why are the keywords usable as a variables?
First, we need to clarify some things for the rest of the answer to make more sense:
async
keyword. There is an async function
construct: MDN, ECMAScript language specifications. So async
only has a special meaning if followed by function - either as an expression (both traditional and an arrow function) or as a declaration. Thus, a variable called async
is valid, since a variable followed by function
is not syntactically valid. There is no clash with semantics:let foo;
//function declaration
foo function bar() {}; //SyntaxError
let foo;
//function expression
let bar = foo function() {}; //SyntaxError
await
, that's actually an operator, not a keyword and it is part of an await
expression (MDN): link to the specs. The semantics require await
to be followed by a unary expression and to be inside an async
function - something that's still not syntactically valid with other variables.I don't have any source for this but it's reasonable to conclude that this has been done to preserve backwards compatibility. If there was code that used async
as a variable in ES5, it would suddenly break later. By making async
only valid where otherwise you get a SyntaxError, that ensures that old code and new code can coexist. Same for await
.
Interestingly, the semantics of await
actually lead to behaviour that is again initially strange - you cannot use it as a variable inside an async
function. Either declaring it:
async function foo() {
let await = "world"; //SyntaxError - not a valid identifier here
}
Nor accessing it:
let await = "world"; //valid identifier
async function foo() {
console.log(await); //SyntaxError - it is not followed by an expression
}
This is initially confusing as well, however, it makes sense for backwards compatibility. Even if pre ES6 code used await
as a variable, it wouldn't have used it in async
functions as those didn't exist. So, old code works and there are still not clashes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With