microsoft / microsoft/TypeScript

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

Ouverte
#61,022 0 commentaires 0 réactions 1 personne assignée Voir sur GitHub

@rbuckton y travaille déjà.

Depuis le 30/1/2025.

Needs Investigation
Langage dominant
Go
Étoiles
111k
Forks
14.3k
Merge moyen
2 j 4 h
PR mergées (30 j)
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
...
```

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.