Repository navigation
Fix remaining unawaited_futures violations - #193135
auto-submit[bot] merged 11 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request wraps numerous asynchronous calls in unawaited or adds linter ignore comments to address the unawaited_futures rule across various tests, benchmarks, and framework files. However, several critical issues were identified where using unawaited instead of await introduces bugs or regressions. Specifically, not awaiting HttpOverrides.runZoned in tests can cause them to pass prematurely without executing assertions, while bypassing await on window resizing, file I/O, and benchmark recording calls can lead to race conditions, missing output files, and inaccurate performance measurements.
| unawaited( | ||
| _profile!.recordAsync('sceneBuildDuration', () async { | ||
| final Scene scene = sceneBuilder.build(); | ||
| unawaited( | ||
| _profile!.recordAsync('windowRenderDuration', () async { | ||
| // On the web, render is asynchronous. | ||
| await (PlatformDispatcher.instance as dynamic).render(scene); | ||
| }, reported: false), | ||
| ); | ||
| }, reported: false), | ||
| ); |
There was a problem hiding this comment.
Not awaiting the nested recordAsync calls means that drawFrameDuration will complete and be recorded before the asynchronous sceneBuildDuration and windowRenderDuration (which performs the actual async render) have finished. This results in inaccurate benchmark measurements. They should be awaited instead of wrapped in unawaited.
await _profile!.recordAsync('sceneBuildDuration', () async {
final Scene scene = sceneBuilder.build();
await _profile!.recordAsync('windowRenderDuration', () async {
// On the web, render is asynchronous.
await (PlatformDispatcher.instance as dynamic).render(scene);
}, reported: false);
}, reported: false);There was a problem hiding this comment.
I was thinking the same thing while reading this code (thought without having tried this myself).
There was a problem hiding this comment.
yup agree with the bot for drawFrameDuration, but the inner most await will cause sceneBuildDuration to also include windowRenderDuration, which I am not sure whether that is something we want. cc @mdebbar do you know if sceneBuildDuration should include windowRenderDuration? if not, we will need to break the async method into 2
There was a problem hiding this comment.
I agree with @chunhtai . I propose the following way:
late final Scene scene;
_profile!.record('sceneBuildDuration', () {
scene = sceneBuilder.build();
}, reported: false);
await _profile!.recordAsync('windowRenderDuration', () async {
// On the web, render is asynchronous.
await (PlatformDispatcher.instance as dynamic).render(scene);
}, reported: false);Because sceneBuildDuration should only record sceneBuilder.build and should not include windowRenderDuration in a nested way.
However, this kind of change is out of the scope of a refactoring PR. I suggest we remove changing this file from this PR and instead do it in a different PR.
There was a problem hiding this comment.
I can split it out of this PR.
unawaited_futures violations across non-frozen codebaseunawaited_futures violations
justinmc
left a comment
There was a problem hiding this comment.
My default approach for these is always to prefer await to ignoring the lint rule where possible, so I left some comments asking about doing that. Did you try these with await already? I imagine lots of them don't have await for a reason, but I'd try them out.
| unawaited( | ||
| _profile!.recordAsync('sceneBuildDuration', () async { | ||
| final Scene scene = sceneBuilder.build(); | ||
| unawaited( | ||
| _profile!.recordAsync('windowRenderDuration', () async { | ||
| // On the web, render is asynchronous. | ||
| await (PlatformDispatcher.instance as dynamic).render(scene); | ||
| }, reported: false), | ||
| ); | ||
| }, reported: false), | ||
| ); |
There was a problem hiding this comment.
I was thinking the same thing while reading this code (thought without having tried this myself).
| unawaited( | ||
| showDialog<void>( |
There was a problem hiding this comment.
I feel like the _showApiDocumentation might make more sense if it returned this Future here. But since it's a private API in a demo, it might not be worth it to change given how it's actually used. I think it's fine either way.
There was a problem hiding this comment.
What is our plan for the references of material in the flutter_gallery? Since we're going to remove material at some point (whether by changing it to material_ui or something non-material), the showDialog here will be replaced by some function that is marked with awaitNotRequired, and thus this unwaited will be awkward to be left here.
There was a problem hiding this comment.
I think I can just await here.
| unawaited( | ||
| _profile!.recordAsync('sceneBuildDuration', () async { | ||
| final Scene scene = sceneBuilder.build(); | ||
| unawaited( | ||
| _profile!.recordAsync('windowRenderDuration', () async { | ||
| // On the web, render is asynchronous. | ||
| await (PlatformDispatcher.instance as dynamic).render(scene); | ||
| }, reported: false), | ||
| ); | ||
| }, reported: false), | ||
| ); |
There was a problem hiding this comment.
yup agree with the bot for drawFrameDuration, but the inner most await will cause sceneBuildDuration to also include windowRenderDuration, which I am not sure whether that is something we want. cc @mdebbar do you know if sceneBuildDuration should include windowRenderDuration? if not, we will need to break the async method into 2
| WidgetTester tester, | ||
| ) async { | ||
| Future<void>.error('Who lives, who dies, who tells your story?'); | ||
| Future<void>.error('Who lives, who dies, who tells your story?'); // ignore: unawaited_futures |
There was a problem hiding this comment.
why using ignore instead of unawaited()?
There was a problem hiding this comment.
/dev/automated_tests/integration_test/exception_handling_expectation.txt is regex matching the exact stacktrace frame, line and column numbers. So if I change it to unawaited the tests will fail or the expected error will be suppressed.
There was a problem hiding this comment.
we should either update the text expectation, or add some comments on why we use ignore here.
| unawaited( | ||
| showDialog<void>( |
There was a problem hiding this comment.
What is our plan for the references of material in the flutter_gallery? Since we're going to remove material at some point (whether by changing it to material_ui or something non-material), the showDialog here will be replaced by some function that is marked with awaitNotRequired, and thus this unwaited will be awkward to be left here.
…ictorsanni/flutter into fix-unawaited-futures-blockers
… reassemble in widget_inspector_test.dart
justinmc
left a comment
There was a problem hiding this comment.
LGTM 👍 Now it looks more like what I was expecting in terms of when to add await and when to ignore. Thanks!
chunhtai
left a comment
There was a problem hiding this comment.
LGTM, one minor suggestion
| WidgetTester tester, | ||
| ) async { | ||
| Future<void>.error('Who lives, who dies, who tells your story?'); | ||
| Future<void>.error('Who lives, who dies, who tells your story?'); // ignore: unawaited_futures |
There was a problem hiding this comment.
we should either update the text expectation, or add some comments on why we use ignore here.
… showDialog in navigator_test
c75a6ba
…erRecorder (#193260) Splits out the `dev/benchmarks/macrobenchmarks/lib/src/web/recorder.dart` fix from #193135 per review discussion (#193135 (comment)). Previously, `SceneBuilderRecorder.onDrawFrame` nested `_profile!.recordAsync('windowRenderDuration', ...)` inside synchronous `_profile!.record('sceneBuildDuration', ...)` without awaiting `recordAsync`, which both triggered `unawaited_futures` and caused `sceneBuildDuration` to include the synchronous portion of `windowRenderDuration`. This PR un-nests the two recordings and makes `onDrawFrame` `async` so `sceneBuildDuration` only measures `sceneBuilder.build()` and `windowRenderDuration` is properly awaited.
Resolves the remaining
unawaited_futuresviolations acrosspackages/flutter/lib/,packages/flutter_driver/,packages/integration_test/,dev/, andpackages/flutter/test/{animation,foundation,services,widgets}in preparation for enabling
unawaited_futuresrepository-wide (#193125).Call sites use
await,unawaited(...), or// ignore: unawaited_futures(where stack trace line/column expectations must be preserved).Mostly Dart SDK functions are causing this. Missed when working on this earlier.
Part of #168555
Pre-launch Checklist
///).