runAsync<T> method

  1. @override
Future<T> runAsync <T>(Future<T> callback(), { Duration additionalTime: const Duration(milliseconds: 1000) })
override

Runs a callback that performs real asynchronous work.

This is intended for callers who need to call asynchronous methods where the methods spawn isolates or OS threads and thus cannot be executed synchronously by calling pump.

If callback completes successfully, this will return the future returned by callback.

If callback completes with an error, the error will be caught by the Flutter framework and made available via takeException, and this method will return a future that completes will null.

Re-entrant calls to this method are not allowed; callers of this method are required to wait for the returned future to complete before calling this method again. Attempts to do otherwise will result in a TestFailure error being thrown.

The additionalTime argument is used by the AutomatedTestWidgetsFlutterBinding implementation to increase the current timeout. See AutomatedTestWidgetsFlutterBinding.addTime for details. The value is ignored by the LiveTestWidgetsFlutterBinding.

Implementation

@override
Future<T> runAsync<T>(Future<T> callback(), {
  Duration additionalTime = const Duration(milliseconds: 1000),
}) {
  assert(additionalTime != null);
  assert(() {
    if (_pendingAsyncTasks == null)
      return true;
    throw test_package.TestFailure(
        'Reentrant call to runAsync() denied.\n'
        'runAsync() was called, then before its future completed, it '
        'was called again. You must wait for the first returned future '
        'to complete before calling runAsync() again.'
    );
  }());

  final Zone realAsyncZone = Zone.current.fork(
    specification: ZoneSpecification(
      scheduleMicrotask: (Zone self, ZoneDelegate parent, Zone zone, void f()) {
        Zone.root.scheduleMicrotask(f);
      },
      createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f()) {
        return Zone.root.createTimer(duration, f);
      },
      createPeriodicTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration period, void f(Timer timer)) {
        return Zone.root.createPeriodicTimer(period, f);
      },
    ),
  );

  addTime(additionalTime);

  return realAsyncZone.run<Future<T>>(() {
    _pendingAsyncTasks = Completer<void>();
    return callback().catchError((dynamic exception, StackTrace stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'Flutter test framework',
        context: 'while running async test code',
      ));
      return null;
    }).whenComplete(() {
      // We complete the _pendingAsyncTasks future successfully regardless of
      // whether an exception occurred because in the case of an exception,
      // we already reported the exception to FlutterError. Moreover,
      // completing the future with an error would trigger an unhandled
      // exception due to zone error boundaries.
      _pendingAsyncTasks.complete();
      _pendingAsyncTasks = null;
    });
  });
}