Creating an external tracking issue so that it can be referenced from package:test.
From an internal investigation:
This is a bug in the SDK and a bad interaction with the fake-zones.
Basically what happens is that the async-SDK caches some common futures to avoid allocating them all the time. Specifically, there is:
/// A `Future<Null>` completed with `null`.
static final _Future<Null> _nullFuture = new _Future<Null>.value(null);
/// A `Future<bool>` completed with `false`.
static final _Future<bool> _falseFuture = new _Future<bool>.value(false);
These are returned from internal classes whenever needed.
Furthermore, there are some optimizations in our Stream implementation. For example (but there are others):
What happens now is that the first time the _nullFuture is used in the test, the current Zone is set to a custom zone that overwrites the scheduleMicrotask to redirect it to testing._FakeAsync.
However, scheduleMicrotask is used for every .then call, and with the fake-async scheduleMicrotask (which needs to be triggered by hand?) the listeners are never executed. This can be easily seen by adding one more scenario to Nate's document:
tearDown(() async {
var f = setUpSubscription.cancel();
f.then((_) { print("null triggered"); });
return f;
});
This will never execute the .then.
The only reason it is sometimes working, is that it hits our optimizations in the stream class. There we don't even call .then but shortcut the execution since we recognize the instance and know that it will just return null.
This also explains most of Nate's scenarios: as soon as you actually call .then on the future you will get a timeout. This can be explicitly or implicitly with await. It only works, if the cancel() future is passed on directly (as an optimization as is the case in my example here), and it then hits our optimization.
For some reason the whole cancel() call must be delayed too, but I haven't tried to figure out why yet. It's probably related to some timing guarantees we give, thus calling .then on the future.
The solution is as simple as creating these static futures in the root-zone. Inside async/future.dart:
static final _Future<Null> _nullFuture = Zone.ROOT.run(() => new
Future<Null>.value(null));
/// A `Future<bool>` completed with `false`.
static final _Future<bool> _falseFuture = Zone.ROOT.run(() => new
Future<bool>.value(false));
The text was updated successfully, but these errors were encountered:
Creating an external tracking issue so that it can be referenced from package:test.
From an internal investigation:
This is a bug in the SDK and a bad interaction with the fake-zones.
Basically what happens is that the async-SDK caches some common futures to avoid allocating them all the time. Specifically, there is:
These are returned from internal classes whenever needed.
Furthermore, there are some optimizations in our Stream implementation. For example (but there are others):
What happens now is that the first time the
_nullFuture
is used in the test, the current Zone is set to a custom zone that overwrites thescheduleMicrotask
to redirect it totesting._FakeAsync
.However,
scheduleMicrotask
is used for every.then
call, and with the fake-async scheduleMicrotask (which needs to be triggered by hand?) the listeners are never executed. This can be easily seen by adding one more scenario to Nate's document:This will never execute the
.then
.The only reason it is sometimes working, is that it hits our optimizations in the stream class. There we don't even call
.then
but shortcut the execution since we recognize the instance and know that it will just returnnull
.This also explains most of Nate's scenarios: as soon as you actually call
.then
on the future you will get a timeout. This can be explicitly or implicitly withawait
. It only works, if thecancel()
future is passed on directly (as an optimization as is the case in my example here), and it then hits our optimization.For some reason the whole
cancel()
call must be delayed too, but I haven't tried to figure out why yet. It's probably related to some timing guarantees we give, thus calling.then
on the future.The solution is as simple as creating these static futures in the root-zone. Inside async/future.dart:
The text was updated successfully, but these errors were encountered: