Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

Transform async functions and await expressions#101

Merged
benjamn merged 4 commits intomasterfrom
async-await
Sep 11, 2014
Merged

Transform async functions and await expressions#101
benjamn merged 4 commits intomasterfrom
async-await

Conversation

@benjamn
Copy link
Copy Markdown
Contributor

@benjamn benjamn commented Apr 3, 2014

This functionality depends on my async-await branch of the Esprima parser (pull request: https://github.com/ariya/esprima/pull/234).

The wrapGenerator.async function is heavily inspired by @lukehoban's spawn function: https://github.com/lukehoban/ecmascript-asyncawait#spawning

Note that Promise is left as a free variable, so you have to bring your own Promise polyfill.

Adding a bunch of tests for this functionality is my next order of business.

cc @amasad @thomasboyt @subtleGradient @bklimt @lukehoban @arv

@benjamn
Copy link
Copy Markdown
Contributor Author

benjamn commented Apr 3, 2014

And here's an example of the transform in action (borrowed from here):

async function chainAnimationsAsync(elem, animations) {
  var ret = null;
  try {
    for (var anim in animations) {
      ret = await anim(elem);
    }
  } catch(e) { /* ignore and keep going */ }
  return ret;
}

becomes

function chainAnimationsAsync(elem, animations) {
  var ret, anim;

  return wrapGenerator.async(function chainAnimationsAsync$($ctx0) {
    while (1) switch ($ctx0.prev = $ctx0.next) {
    case 0:
      ret = null;
      $ctx0.prev = 1;
      $ctx0.t0 = $ctx0.keys(animations);
    case 3:
      if (($ctx0.t1 = $ctx0.t0()).done) {
        $ctx0.next = 10;
        break;
      }

      anim = $ctx0.t1.value;
      $ctx0.next = 7;
      return anim(elem);
    case 7:
      ret = $ctx0.sent;
      $ctx0.next = 3;
      break;
    case 10:
      $ctx0.next = 14;
      break;
    case 12:
      $ctx0.prev = 12;
      $ctx0.t2 = $ctx0.catch(1);
    case 14:
      return $ctx0.abrupt("return", ret);
    case 15:
    case "end":
      return $ctx0.stop();
    }
  }, this, [[1, 12]]);
}

@benjamn
Copy link
Copy Markdown
Contributor Author

benjamn commented Apr 3, 2014

Regenerator doesn't support arrow generator functions yet, because there's no agreed-upon syntax for them, but I definitely intend to add support for async arrow functions.

runtime/dev.js Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: calling this looks awkward, why not just bind to the first arg instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Just trying to shave bytes, that's all.

@amasad
Copy link
Copy Markdown
Contributor

amasad commented Apr 3, 2014

nice! I thought it's going to take a lot more code to implement.
Excited about this! 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"async" is more likely to occur in JS codebases, e.g. the popular library. Maybe 'await' is better to check for?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You can have an async function that has no awaits in it -- but you probably could get away with being more specific a la /\bfunction\s*\*|\basync\s+function\b/

@arv
Copy link
Copy Markdown

arv commented Apr 7, 2014

Very nice

@sophiebits
Copy link
Copy Markdown
Contributor

This looks so awesome.

@lukehoban
Copy link
Copy Markdown

This is awesome.

I tried a more complex example, and ran into a problem with the handling of this:

var headers;
async function foo() {
}

This compiles to the following:

var headers;
(function foo() {
  return wrapGenerator.async(function foo$($ctx0) {
    while (1) switch ($ctx0.prev = $ctx0.next) {
    case 0:
    case "end":
      return $ctx0.stop();
    }
  }, this);
})

Note the extra parentheses added around the function declaration, turning it into a function expression and not binding a global variable to the name foo.

This only happens when a statement like var headers appears before the function declaration.

I expect this is an issue in the esprima additions for await. I tried a similar example with generator functions and do not see this issue.

@lukehoban
Copy link
Copy Markdown

A larger sample working with this is here: https://github.com/lukehoban/ecmascript-asyncawait/blob/regenerator/server.asyncawait.js as part of a branch of the async/await proposal to try to switch to using this regenerator branch for the sample instead of sweet.js macros.

@benjamn
Copy link
Copy Markdown
Contributor Author

benjamn commented Apr 8, 2014

Yep, I can confirm that this is related to my Esprima changes, and Recast is just trying to respect the wrong FunctionExpression type by adding the parentheses.

Will investigate.

@benjamn
Copy link
Copy Markdown
Contributor Author

benjamn commented Apr 8, 2014

@lukehoban Fixed this bug by adding benjamn/esprima@b714306 to my https://github.com/ariya/esprima/pull/234 pull request. In short, I was misusing peekLineTerminator() in matchAsync.

I also switched getCollaboratorImages back to a function declaration in your server.asyncawait.js file, and the transformation did not add parentheses to it.

@benjamn
Copy link
Copy Markdown
Contributor Author

benjamn commented Apr 12, 2014

@lukehoban where did TC39 come down on await*? The notes seem to indicate they don't want it if there's no analogy to yield*, which is fine with me.

@niieani
Copy link
Copy Markdown

niieani commented Aug 13, 2014

Hey everybody, great work @benjamn . I see no movement since April, how's the progress on this?
This is such a nice feature to have!

@benjamn benjamn force-pushed the async-await branch 2 times, most recently from 8f31787 to 5da67bc Compare September 11, 2014 06:44
benjamn added a commit that referenced this pull request Sep 11, 2014
Transform async functions and await expressions.
@benjamn benjamn merged commit b130d87 into master Sep 11, 2014
@sophiebits
Copy link
Copy Markdown
Contributor

Nice!

@jayphelps
Copy link
Copy Markdown

fuck yeah

matthewrobb referenced this pull request in resugar/resugar Sep 27, 2014
@chrisabrams
Copy link
Copy Markdown

👍

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants