microsoft / microsoft/TypeScript
ES2017 Async Generator Suspended on `yield*` Continues Execution When Returned
@rbuckton y travaille déjà.
Depuis le 30/1/2025.
- 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
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Évaluation
Cette issue n'a pas encore été évaluée.