Skip to content

Yielding a promise from async generator #23858

Closed
@agentcooper

Description

@agentcooper

This is a bug report.

It seems that the generated code for async generators is incorrect.

Search Terms: yield, promise, async, generator.

  • TypeScript version: master
  • Node version: 10.0.0
// example.ts

function withDelay(n) {
  return new Promise(resolve => setTimeout(() => resolve(n), 1000));
}

async function* makeGenerator() {
  yield 1;
  yield withDelay(2);
  yield 3;
  yield withDelay(4);
  yield 5;
}

async function pull(gen) {
  const { done, value } = await gen.next();
  console.log('out', value);
  if (!done) {
    await pull(gen);
  }
}

pull(makeGenerator());

Expected behavior:

node example.ts # no TS specific stuff in the code

Output:

out 1
out 2
out 3
out 4
out 5
out undefined

Same behavior in Chrome 66 and Firefox 58.

Actual behavior:

node built/local/tsc.js --lib 'esnext,esnext.asynciterable' example.ts
node example.js

Output:

out 1
out Promise { <pending> }
out 3
out Promise { <pending> }
out 5
out undefined

Playground link

Possible fix:

I was able to fix this by changing __asyncGenerator:

diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts
index 68861a5bb4..9a8721e279 100644
--- a/src/compiler/transformers/esnext.ts
+++ b/src/compiler/transformers/esnext.ts
@@ -909,7 +909,15 @@ namespace ts {
                 function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r);  }
                 function fulfill(value) { resume("next", value); }
                 function reject(value) { resume("throw", value); }
-                function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
+                function settle(f, v) {
+                    if (v.value instanceof Promise) {
+                        v.value.then(function(resolvedValue) {
+                            settle(f, { value: resolvedValue, done: v.done });
+                        });
+                    } else {
+                        if ((f(v), q.shift(), q.length)) resume(q[0][0], q[0][1]);
+                    }
+                }
             };`
     };

Would you be ok with such pull request?

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions