Flutter中的异步执行策略
Posted eclipse_xu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter中的异步执行策略相关的知识,希望对你有一定的参考价值。
点击上方蓝字关注我,知识会给你力量
在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循环的一次迭代中被执行。可以通过这个链接查看更多内容:https://dart.cn/articles/archive/event-loop
Events
任何进入event queue的东西都被称之为Event。这是Flutter中调度异步任务的默认方法。为了调度一个Event,我们把它添加到event queue中,由Event Loop来接收。这种方法被许多Flutter机制所使用,如I/O、手势事件、Timer等。
Timer
Timer是Flutter中异步任务的基础。它被用来安排event queue中的代码执行,无论是否有延迟执行的需要。由此产生的有趣的事实是,如果当前队列很忙,你的定时器将永远不会被执行,即使时间到了。
Timer.run(()
print("Timer");
);
Futureand Future.delayed
Future是Dart中使用的非常广泛的一个异步方法,它的内部实现,实际上也就是基于Timer的。
Future<void>(()
print("Future Event");
);
Future<void>.delayed(Duration.zero, ()
print("Future.delayed Event");
);
它的内部实现如下。
Microtasks
如前所述,所有调度的microtasks都会在下一个调度的Event之前执行。建议避免使用这个队列,除非绝对需要异步执行代码,而且要在event queue的下一个事件之前处理。你也可以把这个队列看成是属于前一个事件的任务队列,因为它们将在下一个事件之前完成。如果这个队列不断膨胀,就会完全冻结你的应用程序,因为它必须先执行这个队列中的所有内容,然后才能进行其事件队列的下一次迭代,例如处理用户输入,甚至渲染应用程序本身。
scheduleMicrotask
顾名思义,在microtask queue中调度一个块代码。与Timer类似,如果出错,会使应用程序崩溃。
scheduleMicrotask(()
print("Microtask");
);
Future.microtask
与我们之前看到的类似,但它将我们的microtask包裹在一个try-catch块中,以一种漂亮而干净的方式返回执行结果或异常。
Future<void>.microtask(()
print("Microtask");
);
它的内部实现如下。
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。
翻译自——https://oleksandrkirichenko.com/blog/delayed-code-execution-in-flutter/
向大家推荐下我的网站 https://www.yuque.com/xuyisheng 点击原文一键直达
专注 android-Kotlin-Flutter 欢迎大家访问
往期推荐
本文原创公众号:群英传,授权转载请联系微信(Tomcat_xu),授权后,请在原创发表24小时后转载。
< END >
作者:徐宜生
更文不易,点个“三连”支持一下👇
以上是关于Flutter中的异步执行策略的主要内容,如果未能解决你的问题,请参考以下文章
Flutter进阶篇(4)-- Flutter的Future异步详解