microsoft / microsoft/TypeScript

ES2017 Async Generator Suspended on `yield*` Continues Execution When Returned

Open
#61,022 0 comments 0 reactions 1 assignee View on GitHub

@rbuckton is already working on this.

Since Jan 30, 2025.

Needs Investigation
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

### 🔎 Search Terms

es2017 async generator delegator await loop yield*

### 🕗 Version & Regression Information

- This is the behavior in every version I tried, and I reviewed the FAQ for entries about async generators

### ⏯ Playground Link

https://www.typescriptlang.org/play/?declaration=false&module=0&ts=5.8.0-dev.20250122#code/PQgEB4CcFMDNpgOwMbVAGwJYCMC8AiaAZ0WgA8AXfUYAPgCh6AKAQyIE8VQmBKUXWqADe9UGJrBQAQQAmLAA4VoM0LEgB7ALYAuUeLAALChXlFtIAO5WAdBXbziySJkXoWiAObX1kD8BnqyETABu4y2OrqANbAMOjQbNAAtIjqSsF2DkROLhRJAExJAMzWRproemJsnMiqAK4oFJjqiABUoB68wpXiYuyY0Ogy7awcXLANyE0tI3wivQvi-YMqAIwA3D29AL48vJtb4sgtROrx1ujqnfgssEqQ+DybO4y9x4hEFKCYX7gdvABtADK7E0EXQ1mqKAAkvcWBQfABdfY9d6nc6XTosCwsH7fCjWUiUXhPHrY3FfH7WGAUOqQRAAfmsKN2KKAA

### 💻 Code

```ts
///

(async () => {
// Adapted from:
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html
async function* g() {
yield* (async function* () {
yield 1;
})();

console.log("after");
}

const it = g()[Symbol.asyncIterator]();
console.log(await it.next());
await it.return?.();
})();
```

### 🙁 Actual behavior

If you run the provided TS code directly in a modern Node process, you get the expected following output:

```
{ value: 1, done: false }
```

If you instead run the JS output code, you get:

```
{ value: 1, done: false }
after
```

Here, the downleveled code behaves differently than the source and continues execution after `.return()` was called in the iterator.

### 🙂 Expected behavior

The generated code should work like the original.

### Additional information about the issue

This only seems to be an issue when `yield*`ing values from another generator. The following works just fine:

```ts
///

(async () => {
async function* g() {
yield* [1, 2];
console.log('after');
}

const it = g()[Symbol.asyncIterator]();
console.log(await it.next());
await it.return?.();
})();
```

---

If you extract the generator into a constant and put the log in a while loop, the loop never exits:

```ts
///

(async () => {
async function* g() {
const x = (async function* () {
yield 1;
})();

for (let i = 0; i < 15; i++) {
yield* x;

console.log('after');
}
}

const it = g()[Symbol.asyncIterator]();
console.log(await it.next());
await it.return?.();
})();
```

```
{ value: 1, done: false }
after
after
after
after
after
...
```

---

If you inline the generator from the above example, the "after" is still logged, but the loop isn't run. Only if you extract the inline generator into a separate constant and only yield that within the loop instead, it keeps going.

```ts
///

(async () => {
async function* g() {
for (let i = 0; i < 15; i++) {
yield* (async function* () {
yield 1;
})();

console.log('after');
}
}

const it = g()[Symbol.asyncIterator]();
console.log(await it.next());
await it.return?.();
})();
```

```
{ value: 1, done: false }
after
```

---

The above does not apply to generators that only `yield*` themselves:

```ts
///

(async () => {
async function* id(x) {
yield* x;
}

async function* g() {
const x = (async function* () {
yield 1;
})();

for (let i = 0; i < 15; i++) {
yield* id(id(x));

console.log('after');
}
}

const it = g()[Symbol.asyncIterator]();
console.log(await it.next());
await it.return?.();
})();
```

```
{ value: 1, done: false }
after
after
after
after
after
...
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.