3

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?

VLAZ
  • 26,331
  • 9
  • 49
  • 67
  • Note that this behavious is consistent with (some) other languages. In C, for example, a variable declaration hides an outer declaration, even if the outer was a function. You can assign to 'printf', for instance. The key is that function names aren't generally 'keywords' (in languages I know about, anyway); they're just identifiers which happen to be the name of a function. – EML Jul 03 '19 at 12:29
  • @EML you still have a similar case in JS. You can re-define the value of `undefined`. Which is annoying but what can you do. `function foo(undefined) { return "hello" === undefined }` would return `true` for `foo("hello")` – VLAZ Jul 03 '19 at 12:33

1 Answers1

13

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.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
  • FYI: `await` is likely to become valid top-level syntax in modules, see [this proposal](https://github.com/tc39/proposal-top-level-await). – T.J. Crowder Jul 03 '19 at 12:22
  • @T.J.Crowder yes, you are correct. I will amend. I meant that it has to be followed by *a* function, not literally `function`. – VLAZ Jul 03 '19 at 12:24
  • The code markup on `function` confused me. **:-)** Good answer. And yes, `async` is not a keyword (and `await` is not a keyword outside `async` functions or -- soon -- modules) because of the need for backward compatibility, which is paramount. (They can do it in modules because when modules were added, [they reserved it](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-future-reserved-words) within modules.) – T.J. Crowder Jul 03 '19 at 12:25
  • *"as for `await`, that's actually an operator, not a keyword"* It's an operator defined by a keyword (like `in` and `typeof`). – T.J. Crowder Jul 03 '19 at 12:27
  • @T.J.Crowder no, you're right. It also confused *me*. I wrote it first wtihout markup, and must have added the markup when revising because I saw "function". I tend to do that... – VLAZ Jul 03 '19 at 12:27
  • Again, this is a very good answer though, my nitpicking is primarily a function of the fact that it's a good answer. **:-)** – T.J. Crowder Jul 03 '19 at 12:28