Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lower async generator functions #3194

Merged
merged 1 commit into from
Jun 25, 2023
Merged

lower async generator functions #3194

merged 1 commit into from
Jun 25, 2023

Conversation

evanw
Copy link
Owner

@evanw evanw commented Jun 25, 2023

With this PR, esbuild will now transform async generator functions into normal generator functions when the configured target environment doesn't support them. These functions behave similar to normal generator functions except that they use the Symbol.asyncIterator interface instead of the Symbol.iterator interface and the iteration methods return promises. Here's an example (helper functions are omitted):

// Original code
async function* foo() {
  yield Promise.resolve(1)
  await new Promise(r => setTimeout(r, 100))
  yield *[Promise.resolve(2)]
}
async function bar() {
  for await (const x of foo()) {
    console.log(x)
  }
}
bar()

// New output (with --target=es6)
function foo() {
  return __asyncGenerator(this, null, function* () {
    yield Promise.resolve(1);
    yield new __await(new Promise((r) => setTimeout(r, 100)));
    yield* __yieldStar([Promise.resolve(2)]);
  });
}
function bar() {
  return __async(this, null, function* () {
    try {
      for (var iter = __forAwait(foo()), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
        const x = temp.value;
        console.log(x);
      }
    } catch (temp) {
      error = [temp];
    } finally {
      try {
        more && (temp = iter.return) && (yield temp.call(iter));
      } finally {
        if (error)
          throw error[0];
      }
    }
  });
}
bar();

This is an older feature that was added to JavaScript in ES2018 but I didn't implement the transformation then because it's a rarely-used feature. Note that esbuild already added support for transforming for await loops (the other part of the asynchronous iteration proposal) a year ago, so support for asynchronous iteration should now be complete.

I have never used this feature myself and code that uses this feature is hard to come by, so this transformation has not yet been tested on real-world code. If you do write code that uses this feature, please let me know if esbuild's async generator transformation doesn't work with your code.

Fixes #2780

@evanw evanw merged commit d77b261 into main Jun 25, 2023
@evanw evanw deleted the async-generator branch June 25, 2023 02:59
@fedellen
Copy link

Thanks for the fix and the write up here ❤️

it
}
export var __yieldStar = value => {
var obj = value[__knownSymbol('asyncIterator')]
Copy link

@tido64 tido64 Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evanw: Hi, I'm currently debugging a crash we're seeing after bumping esbuild to a more recent version where the only diff is the addition of this polyfill. Is this line meant to throw if asyncIterator is not defined/supported by the VM? What is then the reason for the check on line 390 below?

Edit: Would it make sense to switch the order so that we check iterator first then asyncIterator, or is that simply working around the issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support transforming async generators like @babel/plugin-proposal-async-generator-functions does
3 participants