Skip to content

DDC and dart2js have different semantics for awaiting a Future which runs .then callbacks in a different Zone. #50619

@natebosch

Description

@natebosch

cc @lrhn for help with what the expected semantics are for awaiting a Future that behaves in this way.

A typical Future will run the callback pass to .then in whatever Zone the .then call was made in. Angular has an implementation which overrides .then to run _innerFuture.then in a different zone. On the VM and in dart2js, when you await one of these futures the method resumes in the zone that has the _innerFuture.then call, whereas in DDC when you await one of these future the method resumes in the same zone that it started in.

With the following code, both the VM and dart2js will print:

Main: true
Before await: false
After await: true

While DDC will print:

Main: true
Before await: false
After await: false
import 'dart:async';

bool get inRoot => Zone.current == Zone.root;

void main() {
  print('Main: $inRoot');
  var f = ZonedFuture(Future.value(0), Zone.current.run);
  runZoned(() {
    doSomething(f);
  });
}

Future<void> doSomething(Future<void> f) async {
  print('Before await: $inRoot');
  await f;
  print('After await: $inRoot');
}

class ZonedFuture<T> implements Future<T> {
  final Future<T> _innerFuture;
  final T Function<T>(T Function()) _runInZone;

  ZonedFuture(this._innerFuture, this._runInZone);

  @override
  Stream<T> asStream() {
    return _innerFuture.asStream();
  }

  @override
  Future<T> catchError(Function onError, {bool Function(Object error)? test}) {
    return _runInZone(() => _innerFuture.catchError(onError, test: test));
  }

  @override
  Future<S> then<S>(FutureOr<S> Function(T value) onValue,
      {Function? onError}) {
    return _runInZone(() => _innerFuture.then<S>(onValue, onError: onError));
  }

  @override
  Future<T> timeout(Duration timeLimit, {FutureOr<T> Function()? onTimeout}) {
    return _runInZone(() {
      return _innerFuture.timeout(timeLimit, onTimeout: onTimeout);
    });
  }

  @override
  Future<T> whenComplete(FutureOr Function() action) {
    return _runInZone(() => _innerFuture.whenComplete(action));
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions