为什么这个使用 FakeAsync 的测试只是挂在“等待”上,即使未来已经完成?

Dan*_*eny 5 async-await dart flutter

我正在尝试使用 FakeAsync 编写一个测试,但它似乎挂在我的awaits 上。这是一个精简的示例:

  test('danny', () async {
    await FakeAsync().run((FakeAsync async) async {
      print('1');
      final a = Future<bool>.delayed(const Duration(seconds: 5))
          .then((_) => print('Delayed future completed!'))
          .then((_) => true);

      print('2');
      async.elapse(const Duration(seconds: 30));
      // Tried all this too...
      // async.flushMicrotasks();
      // async.flushTimers();
      // async.elapse(const Duration(seconds: 30));
      // async.flushMicrotasks();
      // async.flushTimers();
      // async.elapseBlocking(const Duration(seconds: 30));

      print('3');
      await a;

      print('4');
      expect(1, 2);
    });
  });
Run Code Online (Sandbox Code Playgroud)

此代码输出:

1
2
Delayed future completed!
3
// hangs and never prints '4'
Run Code Online (Sandbox Code Playgroud)

async.elapse呼叫被允许完成未来,但它仍然挂起await a。为什么?

Dan*_*eny 5

发生这种情况似乎是因为虽然Future已完成,但await调用需要处理微任务队列才能继续(但它不能,因为async.elapse在 后没有人调用await)。

作为一种解决方法,在函数运行时持续抽取 microstask 队列似乎可行 - 例如调用此函数代替FakeAsync.run

/// Runs a callback using FakeAsync.run while continually pumping the
/// microtask queue. This avoids a deadlock when tests `await` a Future
/// which queues a microtask that will not be processed unless the queue
/// is flushed.
Future<T> runFakeAsync<T>(Future<T> Function(FakeAsync time) f) async {
    return FakeAsync().run((FakeAsync time) async {
    bool pump = true;
    final Future<T> future = f(time).whenComplete(() => pump = false);
    while (pump) {
        time.flushMicrotasks();
    }
    return future;
    }) as Future<T>;
}
Run Code Online (Sandbox Code Playgroud)

  • @DannyTuppeny 非常感谢。虽然我必须补充一点:这不是处理这个包裹的奇怪方式吗?就像,我们作为消费者,需要手动管理这个“flushMicrotasks”?甚至没有文档提到,如果没有像您所做的那样明确地调用,则永远不会调用此刷新。另外,为什么不建议将此作为“fake_async”包的默认行为? (2认同)