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

Add unnecessary await in return lint #1981

Conversation

annagrin
Copy link
Contributor

Add unnecessary await in return lint:

  • brings performance improvements.

@annagrin annagrin requested a review from elliette February 22, 2023 02:41
@natebosch
Copy link
Member

  • brings performance improvements.

Can you expand on this?

We're considering a language change down the line that would make this await mandatory.

I don't think this is a good lint to use.

@annagrin
Copy link
Contributor Author

  • brings performance improvements.

Can you expand on this?

We're considering a language change down the line that would make this await mandatory.

I don't think this is a good lint to use.

@kevmoo do you know the story of the warning by any chance? Or maybe you can redirect me to someone?

Interesting, thanks Nate for the heads up! I saw flutter using this rule so we also need to coordinate with them in this case.

Could you please redirect me to any documents about the language change? I am also interested in it from the DDC perspective (ie changes that need to be done in DDC, if any).

As for the rule rationale, I couldn't find any dart-specific explanation for the lint (I wish we had the rationale for all lints!) but my guess that the reasoning is similar to the JS one:

https://eslint.org/docs/latest/rules/no-return-await

It is probably implementation-dependent, but it makes sense that the caller is kept in stack until the callee finishes if the return await is present, and popped if there is no await (await on the caller executes the code before the return , pops the stack, then executes await on the call from the return statement), plus you don't need to add extra code for the async/await machinery, which saves time and code size. Let me know if my reasoning is incorrect! I'll hold off on submitting this for now.

@kevmoo
Copy link
Member

kevmoo commented Feb 22, 2023

@annagrin – I'm not sure, honestly. 🤷

@natebosch
Copy link
Member

It is probably implementation-dependent, but it makes sense that the caller is kept in stack until the callee finishes if the return await is present, and popped if there is no await (await on the caller executes the code before the return , pops the stack, then executes await on the call from the return statement), plus you don't need to add extra code for the async/await machinery, which saves time and code size. Let me know if my reasoning is incorrect! I'll hold off on submitting this for now.

I would not expect the JS reasoning to apply in Dart.

@sigmundch might know if there are any perf impacts of omitting await in an async return in JS, but I wouldn't expect noticeable improvements.

Could you please redirect me to any documents about the language change?

dart-lang/language#870

@natebosch
Copy link
Member

The commit introducing this lint doesn't give any reasoning, it looks very plausible to me that it was an arbitrary style choice.

Note that in current implementations the await does have an observable behavior impact when the return is inside a try block. Without the await an error from the future will not be caught.

@@ -367,7 +367,7 @@ class Debugger extends Domain {
// TODO(alanknight): Can these be moved to dart_scope.dart?
final properties = await visibleProperties(debugger: this, frame: frame);
final boundVariables = await Future.wait(
properties.map((property) async => await _boundVariable(property)),
properties.map((property) async => _boundVariable(property)),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
properties.map((property) async => _boundVariable(property)),
properties.map(_boundVariable)

I'd have to measure to be sure, but removing an async along with the await does have a chance at bringing perf improvements. In the cases where it was the only await in the method it could be worth removing along with the async - even if for only style reasons.

This case is nice because it can go further and become a tearoff. I'd make this change independent of the rest of the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do, thanks! I wasn't sure if there is any change in semantics:)

return Future.wait(range
.map((element) async => await _instanceRefForRemote(element.value)));
return Future.wait(
range.map((element) async => _instanceRefForRemote(element.value)));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
range.map((element) async => _instanceRefForRemote(element.value)));
range.map((element) => _instanceRefForRemote(element.value)));

Less interesting case since it doesn't become a tearoff, but if the sole await gets removed it is worth dropping the async too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks Nate, I will skip this PR for now and add the suggested change separately.

@@ -273,7 +273,7 @@ class Locations {
Future<Set<Location>> _locationsForModule(String module) async {
final memoizer = _locationMemoizer.putIfAbsent(module, AsyncMemoizer.new);

return await memoizer.runOnce(() async {
return memoizer.runOnce(() async {
Copy link
Member

Choose a reason for hiding this comment

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

This was the only await, could drop the outer async.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, will do!

@@ -588,7 +588,7 @@ ${globalLoadStrategy.loadModuleSnippet}("dart_sdk").developer.invokeExtension(

@override
Future<ScriptList> getScripts(String isolateId) async {
Copy link
Member

Choose a reason for hiding this comment

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

Could drop async

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do, thanks!

@annagrin
Copy link
Contributor Author

Closing this PR due to the unhelpful lint. I made some manual changes for removing unnecessary asyncs and awaits, as suggested by @natebosch, and some more similar ones: #1982

@annagrin annagrin closed this Feb 22, 2023
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.

3 participants