Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can the `async` and `await` keywords be assigned to? [duplicate]

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?

like image 584
VLAZ Avatar asked Jul 03 '19 12:07

VLAZ


1 Answers

First, we need to clarify some things for the rest of the answer to make more sense:

  • there is no 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
  • as for 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.

like image 108
VLAZ Avatar answered Nov 15 '22 04:11

VLAZ