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

refactor: use async/await syntax #411

Merged
merged 20 commits into from Sep 15, 2016
Merged

Conversation

rpl
Copy link
Member

@rpl rpl commented Aug 4, 2016

The goal of this PR is discussing the following proposals:

  • introducing async / await syntax to make some of the asynchronous functions nicer to read
  • introducing more explicit flow types declarations to extend the kind of statical analysis that flow is able to
    make on the sources

The first patch contains a set of experimental changes related to the usage of the async / await syntax to make long chains of promises more readable, by configuring babel to transpile them into code that can run across the various supported nodejs versions.
This patch ports to the async / await syntax only functions where it makes the code nicer and easier to read, and it doesn't port to the new syntax any of the test cases (mostly because it is better to keep the tests unmodified during such a change).

The second patch introduces more explicit flow types to be able to give to flow the knowledge needed to check the code in much more detail, e.g. by declaring the types of the objects passed as params, signature of the callbacks etc.
The second patch can be augmented with much more explicit flow types definitions, but I wanted to give an initial taste of what it means in terms of the introduced changes before going further in this path. (landed as #424)

As a side note, async/await has been recently moved into Stage 4, and so it seems that it will be officially part of the 2017 version of the standard.

@coveralls
Copy link

coveralls commented Aug 4, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling 8068adb on rpl:refactor/async-await into c73c315 on mozilla:master.

@coveralls
Copy link

coveralls commented Aug 18, 2016

Coverage Status

Coverage decreased (-0.2%) to 99.837% when pulling ea8700e on rpl:refactor/async-await into 6654239 on mozilla:master.

@rpl
Copy link
Member Author

rpl commented Aug 18, 2016

@kumar303 I've just updated this PR by rewriting any function that could benefit from the new async/await syntax, above the changes introduced by #424

The coverall report is currently red because of a small helper that I added into "utils/errors.js", I'm going to write a new small test for it asap.

Besides the (temporary) coverall report failure, this PR should now ready for a review.

@coveralls
Copy link

coveralls commented Aug 18, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling 406aadc on rpl:refactor/async-await into 6654239 on mozilla:master.

@rpl
Copy link
Member Author

rpl commented Aug 18, 2016

@kumar303 I just added the new test case for the isErrorWithCode helper (as 406aadc) which makes coveralls to be green and happy as before ;-)

manifestData = await getValidatedManifest(sourceDir);
}

let buffer = await zipDir(sourceDir, {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra space after the equal sign. Maybe we could add a linting rule for that?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the rule in question is no-multi-spaces.

Copy link
Member Author

Choose a reason for hiding this comment

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

👍

added no-multi-spaces to the enabled eslint rules and removed all the related linting errors in 358c26b

@kumar303
Copy link
Contributor

What happens if some code accidentally calls an async function without using the await keyword? Is there an error? If not, could we add a linting rule for that?

try {
return await connectToFirefox();
} catch (error) {
if (isErrorWithCode('ECONNREFUSED', error)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 for isErrorWithCode(), that reads well

@kumar303
Copy link
Contributor

I'm excited about this! I got halfway through but I have to end for the day. I'll pick it up tomorrow.

@rpl rpl changed the title refactor: use async/await and add more explicit flow types declarations refactor: use async/await syntax Aug 19, 2016
@coveralls
Copy link

coveralls commented Aug 19, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling 358c26b on rpl:refactor/async-await into 6654239 on mozilla:master.

@rpl
Copy link
Member Author

rpl commented Aug 19, 2016

What happens if some code accidentally calls an async function without using the await keyword? Is > there an error? If not, could we add a linting rule for that?

@kumar303 sorry, I forgot to mention this in the summary of this pull request, and I totally agree that it is an important detail that needs to be discussed in this pull request:

An "async function" always returns a Promise, the returned promise is resolved to the value return-ed by the async function, if an exception is thrown in the body of the async function (and there is no try/catch to stop it), then the returned promise is rejected and the exception passed as parameter of the Promise error callback.

This is very nice, because it means that async function can be composed into Promise chains and mixed with non-async function which returns a Promise (e.g. it is completely valid to put the result of an async function into a Promise.all array), but it probably makes a bit harder to decide if the await keyword is missing by mistake or intentionally, e.g.:

let res = asyncFunction01(...);
let res2 = asyncFunction02(...);

await Promise.all([res, res2]);

versus

let res = await asyncFunction01(...);
...
let res2 = await asyncFunction02(...);
...

Nevertheless, there is an eslint-plugin-async-await npm package that has two rules that can be interesting, because they enforce a space where async/await can be confused for a function name, e.g.:

// This is valid for babel, but it can be confusing
let res = await(asyncFunction01(...)); 

// is what the `eslint-plugin-async-await` can help to enforce
let res = await (asyncFunction01(...));  

@rpl
Copy link
Member Author

rpl commented Aug 19, 2016

but it probably makes a bit harder to decide if the await keyword is missing by mistake or intentionally

to be completely fair, what I think is that probably eslint will not be able to detect this kind of mistakes... but I expect that most of them should be caught by the flow type checks :-D

e.g. Flow should be able to detect when we are trying to use a promise as the result of "awaiting a promise resolved value", as well as if we are trying to put into a Promise.all something that is not a promise, and if it doesn't detect it, now we know how to look into the flow coveraging info ;-)

@coveralls
Copy link

coveralls commented Aug 19, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling ff12c02 on rpl:refactor/async-await into 6654239 on mozilla:master.

@coveralls
Copy link

coveralls commented Aug 19, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling cdcbc92 on rpl:refactor/async-await into 6654239 on mozilla:master.

@coveralls
Copy link

coveralls commented Sep 15, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling bdb10bf on rpl:refactor/async-await into 0d1b824 on mozilla:master.

@coveralls
Copy link

coveralls commented Sep 15, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling 4903d57 on rpl:refactor/async-await into 7fac094 on mozilla:master.


let client;

while (retriesLeft >= 0) {
Copy link
Member Author

Choose a reason for hiding this comment

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

@kumar303 This is the "async/await"-based version of the new defaultRemotePortFinder (which is the bigger change after the rebase, and it worth a second look from an additional pair of eyes ;-))

Copy link
Contributor

Choose a reason for hiding this comment

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

This looks great to me.

try {
client = await connectToFirefox(portToTry);
log.debug(`Remote Firefox port ${portToTry} is in use ` +
`(retries remaining: ${retriesLeft} )`);
Copy link
Contributor

Choose a reason for hiding this comment

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

while you're at it, you could fix the extra space in the parentheses around ${retriesLeft} :) no biggie though, just looks odd in the console.

Copy link
Member Author

Choose a reason for hiding this comment

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

👍 small tweak just pushed

@coveralls
Copy link

coveralls commented Sep 15, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling e90c497 on rpl:refactor/async-await into 7fac094 on mozilla:master.

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.

None yet

5 participants