Async and Generator Function Constructors in JavaScript
Tags: javascript, programming
Published: 2023-09-17 04:43:05

Sometimes I like to browse through the [ECMAScript/JavaScript specification](https://tc39.es/ecma262/) or [the HTML specification](https://html.spec.whatwg.org/) at random, to see if I come across anything interesting or unexpected. That's how I first became aware of [the "structured cloning" algorithm](https://stackoverflow.com/a/10916838/1114) (back when it was still only for internal use), and today I found a new piece of JavaScript trivia that really tickled my interest.

### The Background

In JavaScript, if you want to define a new function dynamically/at runtime, from source code, you can do so by using [the `Function` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function). For example, if we want to define a function that adds two numbers, we could do so like this:

```javascript
const add = new Function("a", "b", "return a + b;");
```

This would create a function equivalent to as if we had written this:

```javascript
const add = function(a, b) { return a + b; }
```

In practice, this is essentially **never** a good idea. It's basically the same as using [`eval`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval), with an unreasonable risk of introducing security issues or negatively affecting the performance of your program, as well as preventing effective static analysis by tools like TypeScript. It has been widely discouraged for a very long time, and is entirely blocked by [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) configurations that are enabled by default by many modern web frameworks. But let's set these concerns aside for a moment.

Later versions of ECMAScript added three new types of functions, which are declared using slightly different syntaxes. Here are contrived examples of each:

```javascript
// Async Functions:
const add = async function(a, b) {
  return (await a) + (await b);
};

// Generator Functions:
const add = function*(a, b) {
  yield a + b; 
}

// Async Generator Functions:
const add = async function*(a, b) {
  yield (await a) + (await b);
};
```

You can see that each different type of function requires a different syntax where the `function` keyword appears. However, the `Function` constructor only lets us specify the contents of the argument list and body; it doesn't provide any way to affect the `function` keyword, so it can't be used to create these new types of functions. And if we try in the JavaScript console, we don't see any `AsyncFunction`, `GeneratorFunction`, or `AsyncGeneratorFunction` constructors we can use instead: none of those identifiers are declared in the global namespace.

```
Uncaught ReferenceError: AsyncFunction is not defined
```

This isn't really surprising. These types of functions were added to the language long after use of the `Function` constructor was already widely discouraged. Although the existing constructor could never be removed from the language due to backwards-compatibility concerns, it's understandable that they wouldn't want to introduce new ones to allow these patterns to be continued in new code being written in the modern versions of the language.

### The Surprise

But much to my surprise, the ECMAScript specification _does_ define behavior for each of these constructors, even though _they're not directly exposed under any name_. We can find these in the 2024 edition of the specification under [section 27.7.1 for "The `AsyncFunction` Constructor"](https://tc39.es/ecma262/#sec-async-function-constructor), [section 27.3.1 for "The `GeneratorFunction` Constructor"](https://tc39.es/ecma262/#sec-generatorfunction-constructor), and [section 27.4.1 for "The `AsyncGeneratorFunction` Constructor](https://tc39.es/ecma262/#sec-asyncgeneratorfunction-constructor).

However, as mentioned, these aren't directly exposed under any name; the corresponding identifiers are not declared in the global namespace. So are they just an irrelevant internal detail of the spec?

Nope! For better or worse, we can actually use them. Although they're not exposed under any name, we can get references to these constructors through [the `.constructor` property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) of existing functions of each type, with code like this:

```javascript
const AsyncFunction = (async function() {}).constructor;
const GeneratorFunction = (function*() {}).constructor;
const AsyncGeneratorFunction = (async function*() {}).constructor;
```

With these definitions available, we can dynamically create functions of these new types just like we can create normal functions:

```javascript
const add = new AsyncFunction("a", "b",
  "return (await a) + (await b);"
);

console.log(await add(Promise.resolve(4), Promise.resolve(2)));
```

```none
6
```

Should you ever do this? Almost certainly not. This type of code is discouraged for good reasons. Frontend code should be using a `Content Security Policy` that forbids `eval`-style code like this from even running. I'm mostly just sharing this because I thought it was a fun piece of trivia, and I was surprised that it was included in the specification, even though it does make a certain kind of sense for consistency.

But if you really do find yourself in a situation where you actually need this capability (maybe you're writing some kind of custom JavaScript REPL), now you know that it exists!