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

Support Async/Await based on #12 #125

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

shirotech
Copy link
Contributor

@shirotech shirotech commented Apr 10, 2018

It's been a very long time since there has been activity to this pull request #12 I have cleanly integrated the code using latest master with minor improvements and seems to pass all tests. Hope this is appreciated and merged to codebase as I believe many have requested this feature.

Closes #90, Closes #108, Closes #109, Closes #113

@shirotech
Copy link
Contributor Author

Now that I think about it, should it be the responsible of buble to transpile async code by default? I’m happy to introduce a “dangerous” flag for it? What do you guys think?

@aleclarson
Copy link
Contributor

aleclarson commented May 14, 2018

Need some tests for:

  • const res = await promise;
  • return (await promise).prop;
  • let val = foo(); val = await bar(val); val = mux(val + 1);

Also, in this test case, could we get thing() and stuff() calls into the same function as the await statements after each of them?

{
description: 'transpiles await arrow function call',
input: `async () => await a()`,
output: `!function() { return Promise.resolve().then(function() { return a() }); }`
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the point of !function? Just to draw attention that it's not being used?

Copy link

@maxmilton maxmilton Aug 31, 2018

Choose a reason for hiding this comment

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

It turns it from a function declaration into an expression, see: https://stackoverflow.com/questions/3755606/what-does-the-exclamation-mark-do-before-the-function

As for why I'm not entirely sure.

Copy link
Member

Choose a reason for hiding this comment

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

The input is a function expression, too.

@mrTimofey
Copy link

Is this going to be released some day? It's kinda good stuff but seems like no one is going to merge and release it...

@aleclarson
Copy link
Contributor

@mrTimofey Lots of edge cases are unaccounted for in this PR, and the author is MIA.

@KutnerUri
Copy link

KutnerUri commented Oct 14, 2018

please add test making sure this works:
const { a, b } = await abFunc();

this doesn't work correctly in Bable, and it caused some nasty bugs for us.

description: 'transpiles await function call with more than one line of code',
input: `async function f() { await a(); thing(); await a2(); stuff(); await a3(); await a4(); }`,
output: `function f() { return Promise.resolve().then(function() { return a(); }) .then(function() { thing(); }) .then(function() { return a2(); }) .then(function() { stuff(); }) .then(function() { return a3(); }) .then(function() { return a4(); }).then(function() {}) }`
}
Copy link
Contributor

Choose a reason for hiding this comment

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

there is not test that verifies return values and closures

	{
		description: 'transpiles await function call with normal return statement',
		input: `async function f() { var x = 1; await a(); return x; }`,
        output: `...`
	},

@shirotech
Copy link
Contributor Author

Sorry guys seems this PR is far from perfect, I have only taken it from an existing PR. Will try my best to resolve.

src/support.js Outdated
}
};

export const features = [
'arrow',
'asyncAwait',
'classes',
'computedProperty',
'conciseMethodProperty',
Copy link
Member

Choose a reason for hiding this comment

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

This whole file can stay as it is, since I already added asyncAwait.

src/index.js Outdated
@@ -15,8 +15,8 @@ const dangerousTransforms = ['dangerousTaggedTemplateString', 'dangerousForOf'];
export function target(target) {
const targets = Object.keys(target);
let bitmask = targets.length
? 0b11111111111111111111
: 0b01000000000000000000;
Copy link
Member

Choose a reason for hiding this comment

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

This change is not necessary anymore.

.eslintrc Outdated
@@ -5,6 +5,7 @@
"semi": [ 2, "always" ],
"keyword-spacing": [ 2, { "before": true, "after": true } ],
"space-before-blocks": [ 2, "always" ],
"space-in-parens": [ 2, "never" ],
"no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
"no-cond-assign": [ 0 ]
},
Copy link
Member

Choose a reason for hiding this comment

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

This should be a separate change.

Copy link
Contributor

@curran curran left a comment

Choose a reason for hiding this comment

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

Needs more work here but excited to see the feature land some day!

const last = this.body[this.body.length - 1];
const hasOnlyOneLine = this.body.length === 1;

// TODO refactor :)
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor how? Who wrote this comment?

Better refactor now before merging, as later it will most likely not ever be refactored.

}

} else if (this.parent.type === 'ArrowFunctionExpression') {
// TODO merge with ^
Copy link
Contributor

Choose a reason for hiding this comment

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

TODOs should be new issues IMO, and not included in the code.

export default class AwaitExpression extends Node {
static removeAsync(code, transforms, async, start, callback = noop) {
if (transforms.asyncAwait && async) {
code.remove(start, start + 6);
Copy link
Contributor

Choose a reason for hiding this comment

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

Magic number 6 is mysterious here. Better as named variable, or with a comment?

Choose a reason for hiding this comment

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

could this be "async ".length?

@dy
Copy link

dy commented Nov 15, 2018

Is this ever going to be merged?

@curran
Copy link
Contributor

curran commented Nov 15, 2018

@dy It looks like the author of this PR @Van-Nguyen is not able to put in more time here and wrap it up. Someone else probably needs to make a new PR based on this one, and clean it up. I'm not sure who.

@adrianheine
Copy link
Member

I've rebased the commit and already merged the unrelated linting part as acd791f.

const hasOnlyOneLine = this.body.length === 1;

// TODO refactor :)
if (this.parent.type === 'FunctionDeclaration') {
Copy link
Member

Choose a reason for hiding this comment

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

This does not handle FunctionExpressions.

if (this.parent.type === 'FunctionDeclaration') {
if (hasOnlyOneLine) {
if (first.type === 'ReturnStatement') {
code.insertLeft(first.argument.start, 'Promise.resolve().then(function() { ');
Copy link
Member

Choose a reason for hiding this comment

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

insertLeft has been replaced by appendLeft.


transpile (code, transforms) {
AwaitExpression.removeAsync(code, transforms, true, this.start, () => {
code.insertLeft(this.argument.start, 'return ');
Copy link
Member

Choose a reason for hiding this comment

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

This handles the expression as though it was a statement. See for example async () => await 1 + await 2.

@adrianheine
Copy link
Member

@KutnerUri @aleclarson Could you open pull requests adding tests for the cases you mentioned to test/samples/async.js? I'd like to see this move forward at least a bit.

@KutnerUri
Copy link

@adrianheine - ok!
this is actually a great way for me to get started.
Don't you mean adding the tests at test/samples/async-await.js ? it seems more suited.

thinking about the tests, I think variables should be hoisted to the top of the function, otherwise it is going to be very difficult to transpile.

I think we need to add 'run code' tests, with expected results instead of expected transpiled.
This would be more abstract on how the code is transpiled.

@adrianheine
Copy link
Member

@KutnerUri No, there is already a file test/samples/async.js in master, that's where these tests should land. As for tests that execute code, I'm already doing this with test262, see scripts/run_test262.js. I think we need unit tests that check the exact code produced, but I understand that they are difficult to write before the feature itself is implemented. Maybe you start with rather simple cases where it's obvious what the resulting code should look like?

@KutnerUri
Copy link

yes, tests on the produced code are a must, but the code should also pass correctness tests.
I am already writing my use case.

so should I just replace errors in async.js with the expected produced code?

@KutnerUri
Copy link

KutnerUri commented Nov 16, 2018

ok so I went ahead and did that. But I don't seem to be able to push?
not to a new branch or pr-125.

Should I clone the project and send a pull request to master or something like that?

@adrianheine
Copy link
Member

Yes, you have to clone the project and open a PR to master.

@KutnerUri
Copy link

KutnerUri commented Nov 18, 2018

created - #170
I will add more tests when I have the time.

I am thinking about await outside a an async function:
This makes sense:

async function f(){
    return await Promise.resolve(3);
}

But this does not:

const a = await Promise.resolve(3);

It breaks code symmetry, and will require scaffolding when working with code fragments.

So, I'm suggesting that Bublé will support this, but with a flag. It could be topLevelAwait or standaloneAwait:

const { code } = Buble.transform('const a = await f();', {transforms:{toLevelAwait: true}});
console.log(code);
//var a; Promise.resolve().then(function() { return f(); }).then(function(){ a= f(); return a; }) ; 

@adrianheine
Copy link
Member

Current master should already support top-level await. I don't think there's need for an option.

@leeoniya
Copy link

not to derail the effort here, but there's a babel plugin with extensive support that might be easier to port than duplicating the effort to add all cases it already handles?

https://github.com/rpetrich/babel-plugin-transform-async-to-promises

@aleclarson
Copy link
Contributor

aleclarson commented Apr 24, 2020

When running yarn test on this PR, I get this error:

Transforming async functions is not implemented. Use `transforms: { asyncAwait: false }` to skip transformation and disable this error. (1:0)

@adrianheine Do you know how I could fix this?

edit: Nevermind, I think I can figure it out.

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