在Flutter中,如何履行一段推迟履行的异步代码?咱们能够找到下面这些办法。

  • scheduleMicrotask
  • Future.microtask
  • Future
  • Future.delayed
  • Timer.run
  • WidgetsBinding.addPostFrameCallback
  • SchedulerBinding.addPostFrameCallback

你或许会说,这是相当多的选择,但是它们彼此之间有些什么异同呢?

Event Loop and Multithreading

Dart是一个单线程模型。但是你的Flutter运用相同能够一起做多件工作,这便是「Event Loop」发挥作用的地方。Event Loop是一个无尽的循环,它履行预订的events。这些events(或许仅仅代码块)必须是轻量级的,否则,你的运用程序会感觉卡顿。

每个event,如按下按钮或网络请求,都被安排在一个工作行列中,等候被工作循环捡起并履行。这种规划形式在UI和其他处理任何类型工作的系统中相当常见。

在Dart的单线程模型中,还有一个Microtask。它组成了Event Loop中的另一一个行列,即Microtask Queue。关于这个行列你唯一需求记住的是,在工作本身被履行之前,一切安排在Microtask Queue的使命都将在Event Loop循环的一次迭代中被履行。

Flutter中的异步执行策略
能够经过这个链接查看更多内容:dart.cn/articles/ar…

Events

任何进入event queue的东西都被称之为Event。这是Flutter中调度异步使命的默许办法。为了调度一个Event,咱们把它添加到event queue中,由Event Loop来接收。这种办法被许多Flutter机制所运用,如I/O、手势工作、Timer等。

Timer

Timer是Flutter中异步使命的根底。它被用来安排event queue中的代码履行,不管是否有推迟履行的需求。由此产生的风趣的事实是,假如当时行列很忙,你的定时器将永远不会被履行,即使时间到了。

Timer.run(() {
    print("Timer");
});

Future and Future.delayed

Future是Dart中运用的非常广泛的一个异步办法,它的内部完结,实际上也便是根据Timer的。

Future<void>(() {
    print("Future Event");
});
Future<void>.delayed(Duration.zero, () {
    print("Future.delayed Event");
});

它的内部完结如下。

Flutter中的异步执行策略

Microtasks

如前所述,一切调度的microtasks都会鄙人一个调度的Event之前履行。主张防止运用这个行列,除非绝对需求异步履行代码,并且要在event queue的下一个工作之前处理。你也能够把这个行列看成是属于前一个工作的使命行列,因为它们将鄙人一个工作之前完结。假如这个行列不断膨胀,就会彻底冻结你的运用程序,因为它必须先履行这个行列中的一切内容,然后才干进行其工作行列的下一次迭代,例如处理用户输入,乃至烘托运用程序本身。

scheduleMicrotask

顾名思义,在microtask queue中调度一个块代码。与Timer相似,假如出错,会使运用程序溃散。

scheduleMicrotask(() {
    print("Microtask");
});

Future.microtask

与咱们之前看到的相似,但它将咱们的microtask包裹在一个try-catch块中,以一种漂亮而干净的方式回来履行成果或异常。

Future<void>.microtask(() {
    print("Microtask");
});

它的内部完结如下。

Flutter中的异步执行策略

Post Frame Callback

前面两种办法只涉及到lower-level Event Loop,而现在咱们要转到Flutter领域。这个Callback会在烘托管道完结时被调用,所以它与widget的生命周期相办理。当它被调度时,它只会被调用一次,而不是在每一帧都回调。运用addPostFrameCallback办法,你能够安排一个或多个回调,在界面烘托完结后被调用。

一切预订的Callback将在frame结束时依照它们被添加的顺序履行。到这个回调被调用的时候,能够保证Widget的构建进程现已完结。经过一些办法,你乃至能够拜访Widget(RenderBox)的布局信息,比方它的大小,并做其他的一些工作。Callback本身将在正常的event queue中运行,Flutter默许运用该行列来处理简直一切工作。

SchedulerBinding

这是一个担任绘图回调的mixin类,完结了咱们感兴趣的办法。

SchedulerBinding.instance.addPostFrameCallback((_) {
    print("SchedulerBinding");
});

WidgetsBinding

我特意包含这个,因为它经常和SchedulerBinding一起被提及。它从SchedulerBinding中承继了这个办法,并有与咱们的主题无关的一些额外办法。一般来说,你运用SchedulerBinding或WidgetsBinding并不重要,两者将履行位于SchedulerBinding中的彻底相同的代码。

WidgetsBinding.instance.addPostFrameCallback((_) {
    print("WidgetsBinding");
});

总结

因为咱们今日学到了很多理论知识,我强烈主张咱们多玩一瞬间,以确保咱们能正确地把握它。咱们能够在之前的initState中运用下面的代码,并尝试预测它将以何种顺序被履行,这并不是一件看起来很容易的工作。

SchedulerBinding.instance.addPostFrameCallback((_) {
  print("SchedulerBinding");
});
WidgetsBinding.instance.addPostFrameCallback((_) {
  print("WidgetsBinding");
});
Timer.run(() {
  print("Timer");
});
scheduleMicrotask(() {
  print("scheduleMicrotask");
});
Future<void>.microtask(() {
  print("Future Microtask");
});
Future<void>(() {
  print("Future");
  Future<void>.microtask(() {
    print("Microtask from Event");
  });
});
Future<void>.delayed(Duration.zero, () {
  print("Future.delayed");
  Future<void>.microtask(() {
    print("Microtask from Future.delayed");
  });
});

输出成果如下所示。

I/flutter (31989): scheduleMicrotask
I/flutter (31989): Future Microtask
I/flutter (31989): SchedulerBinding
I/flutter (31989): WidgetsBinding
I/flutter (31989): Timer
I/flutter (31989): Future
I/flutter (31989): Microtask from Event
I/flutter (31989): Future.delayed
I/flutter (31989): Microtask from Future.delayed

现在咱们了解了这么多细节,你能够对如何安排你的代码做出深思熟虑的决议。作为一个经验规律,假如你需求你的上下文或与Layout或UI相关的东西,请运用addPostFrameCallback。在任何其他情况下,用Future或Future.delayed在规范的event queue中进行调度应该是足够的。microtask queue是非常小众的东西,你或许永远不会遇到,但它仍然值得了解。当然,假如你有一个繁重的使命,你就会考虑创建一个Isolate。

翻译自——oleksandrkirichenko.com/blog/delaye…

欢迎咱们关注我的大众号——【群英传】,专心于「Android」「Flutter」「Kotlin」 我的语雀知识库——www.yuque.com/xuyisheng