Delinting future awaits round 2 (#10790) * round 2 * deal with null futures * review * review * review * review
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index 8a688b2..7b97bf6 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -15,6 +15,7 @@ import '../base/port_scanner.dart'; import '../base/process.dart'; import '../base/process_manager.dart'; +import '../base/utils.dart'; import '../build_info.dart'; import '../commands/build_apk.dart'; import '../device.dart'; @@ -452,8 +453,10 @@ printError('Error waiting for a debug connection: $error'); return new LaunchResult.failed(); } finally { - observatoryDiscovery.cancel(); - diagnosticDiscovery.cancel(); + await waitGroup<Null>(<Future<Null>>[ + observatoryDiscovery.cancel(), + diagnosticDiscovery.cancel(), + ]); } } @@ -524,8 +527,10 @@ 'shell', 'am', 'broadcast', '-a', 'io.flutter.view.DISCOVER' ])); - await new Future<Null>.delayed(const Duration(seconds: 1)); - logs.cancel(); + await waitGroup<Null>(<Future<Null>>[ + new Future<Null>.delayed(const Duration(seconds: 1)), + logs.cancel(), + ]); return result; } }
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart index 5b7f99b..902807a 100644 --- a/packages/flutter_tools/lib/src/android/android_workflow.dart +++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -11,6 +11,7 @@ import '../base/platform.dart'; import '../base/process.dart'; import '../base/process_manager.dart'; +import '../base/utils.dart'; import '../doctor.dart'; import '../globals.dart'; import 'android_sdk.dart'; @@ -189,9 +190,12 @@ <String>[sdkManagerPath, '--licenses'], environment: sdkManagerEnv, ); - stdout.addStream(process.stdout); - stderr.addStream(process.stderr); - process.stdin.addStream(stdin); + + waitGroup<Null>(<Future<Null>>[ + stdout.addStream(process.stdout), + stderr.addStream(process.stderr), + process.stdin.addStream(stdin), + ]); final int exitCode = await process.exitCode; return exitCode == 0;
diff --git a/packages/flutter_tools/lib/src/base/process.dart b/packages/flutter_tools/lib/src/base/process.dart index 0d51386..b3b4169 100644 --- a/packages/flutter_tools/lib/src/base/process.dart +++ b/packages/flutter_tools/lib/src/base/process.dart
@@ -9,6 +9,7 @@ import 'file_system.dart'; import 'io.dart'; import 'process_manager.dart'; +import 'utils.dart'; typedef String StringConverter(String string); @@ -156,12 +157,15 @@ // Wait for stdout to be fully processed // because process.exitCode may complete first causing flaky tests. - await Future.wait(<Future<Null>>[ + await waitGroup<Null>(<Future<Null>>[ stdoutSubscription.asFuture<Null>(), stderrSubscription.asFuture<Null>(), ]); - stdoutSubscription.cancel(); - stderrSubscription.cancel(); + + await waitGroup<Null>(<Future<Null>>[ + stdoutSubscription.cancel(), + stderrSubscription.cancel(), + ]); return await process.exitCode; }
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart index fddf40c..af7b8b8 100644 --- a/packages/flutter_tools/lib/src/base/utils.dart +++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -256,3 +256,14 @@ _timer = null; } } + +/// Returns a [Future] that completes when all given [Future]s complete. +/// +/// Uses [Future.wait] but removes null elements from the provided +/// `futures` iterable first. +/// +/// The returned [Future<List>] will be shorter than the given `futures` if +/// it contains nulls. +Future<List<T>> waitGroup<T>(Iterable<Future<T>> futures) { + return Future.wait<T>(futures.where((Future<T> future) => future != null)); +}
diff --git a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart index dac4347..86f1818 100644 --- a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart +++ b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
@@ -172,7 +172,8 @@ printTrace('dart ${command.skip(1).join(' ')}'); _process = await processManager.start(command); - _process.exitCode.whenComplete(() => _process = null); + // This callback hookup can't throw. + _process.exitCode.whenComplete(() => _process = null); // ignore: unawaited_futures final Stream<String> errorStream = _process.stderr.transform(UTF8.decoder).transform(const LineSplitter()); errorStream.listen(printError);
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index f5d997f..605876d 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -394,7 +394,9 @@ if (options.debuggingEnabled) { connectionInfoCompleter = new Completer<DebugConnectionInfo>(); - connectionInfoCompleter.future.then<Null>((DebugConnectionInfo info) { + // We don't want to wait for this future to complete and callbacks won't fail. + // As it just writes to stdout. + connectionInfoCompleter.future.then<Null>((DebugConnectionInfo info) { // ignore: unawaited_futures final Map<String, dynamic> params = <String, dynamic>{ 'port': info.httpUri.port, 'wsUri': info.wsUri.toString(), @@ -405,7 +407,9 @@ }); } final Completer<Null> appStartedCompleter = new Completer<Null>(); - appStartedCompleter.future.then<Null>((Null value) { + // We don't want to wait for this future to complete and callbacks won't fail. + // As it just writes to stdout. + appStartedCompleter.future.then<Null>((Null value) { // ignore: unawaited_futures _sendAppEvent(app, 'started'); });