Flutter和Dart的异步编程
参考:
- https://medium.com/@takahirom/how-to-write-flutter-asynchronous-processing-22f845204f30
- https://www.youtube.com/watch?v=MUDOIAssBDs&feature=youtu.be
使用Flutter和Dart开发APP或者应用时,通常需要访问DB、API、或其他资源,都需要异步处理进行性能提升。
作为Flutter开发组件库,提供了两个组件直接支持异步的UI构建处理(https://flutter.io/widgets/async/)。
- FutureBuilder: 基于Future的最新数据来构建Widget。
- StreamBuilder: 基于数据流Stream的数据来构建Widget(可以支持多个数据持续更新构建,BLOC的架构就是依次模式构建)。
Dart Asynchronous 基础
Future表示一个数据还没有存在:
1 | abstract class Future<T> { |
Stream标识一个异步的迭代对象,有顺序数据、可以操作和过滤数据;迭代是拉(Pull)模式访问数据,Stream是推(Push)模式访问数据。
1 | abstract class Stream<E>{ |
Stream & Future例子:
1 | runServer(){ |
问题是使用了太多的Callback级联,通过使用await语法糖可以简化,清晰逻辑。
过渡方案,使用for循环优化例子:
1 | runServer(){ |
await优化的例子:
1 | runServer() async{ |
await for继续优化:
1 | runServer() async{ |
关于Await的用法,对于Future进行等待期执行结果,在代码块(方法)内,await把Future的异步处理变成了同步调用关系(Process);同时await返回Future的泛型结果数据。
1 | API: |
await也可以使用在非Future对象上的任意变量上,等价于Future的value包装。
1 | await 998; |
闭包的使用:
1 | foo(() async => ...); |
Bodywrap与microtask:
1 | Future runServer() async {...} |
Bodywrap的运行结果和影响(Consequences):
- 在代码块中的错误会被自行捕获,并返回到Future中;
- Functions yield at entry (???)
- 返回的Futures可以被链条处理(chained);
- 返回结果是Future类型
异步返回多个数据,需要使用Stream,一个数据的时候使用Future。同步的时候使用Iterable返回多个数据。
同步多个数据迭代处理(Iterable & sync*):
1 | Iterable range(int from, int to) sync* { |
yield推送一个数据,yeild*在另外一个迭代之间建立管道,推送数据到上层迭代。
1 | Iterable recRange(int from, int to){ |
异步返回多个数据到Streams中,使用async*:
1 | Stream bigFiles(List<String> fileNames) async* { |
Async*的内部细节:
- 函数直到listen方法调用的时候才会开始执行;
- cancel方法可以终结async函数;
- Yield通过event loop返回数据。
1 | while (true) { yield 988 } |
异步编程的问题:
- Debug调试困难
- 处理交错执行
- 堆栈帧丢失
- 当Debug时,stacktrace有太多的Noice,解决办法,使用stack_trace包简化调用处理:
1 | import 'package:stack_trace/stack_trace.dart'; |
Dart Asynchronous Processing
Dart语言支持异步处理流程,通常的用法有如下7种模式。
1 | import 'dart:async'; |
关于什么时候使用Future,什么时候使用Stream。
对于多个数据的异步处理流程,只能使用Stream,对于一般异步处理使用Future和Then更加有利于代码逻辑组织。
1 | Future<String> asyncFunc() async { |
This allows you to choose whether to use FutureBuilder or StreamBuilder depending on your situation.
“await for” 和 “listen()”
在大多数场景,使用 wait for会应该更清晰表达逻辑。
1 | import 'dart:async'; |
但是使用await和listen还是有区别,一个是同步流程,一个是异步流程。例如下面这个例子,使用await,后一个countUp
永远也不会被执行到。
1 | import 'dart:async'; |
当然,单独的处理封装成异步方法,再调用,就可以把await的操作又异步化组合一起了,但是有这个必要吗?这个时候也许使用listen更好一下吧,具体看情况而定。
1 | import 'dart:async'; |
这是用listen的例子,是不是在这种状况下会更好一些。
1 | import 'dart:async'; |
注意 在Stream上使用await for
的时候,一定要清晰认识到你是否需要等待所有的Stream数据都到达处理完后再执行后续操作。这个在很多场合都是不适用的,例如侦听Dom的事件,如果对Dom两个节点进行侦听事件的处理,就不能适用await for
,而应该使用listen
异步侦听模式。
简单的说,await for
是同步拆解出所有的stream数据并处理,在特定场景需要这样做,另外一些场景缺不能这样做。
“await” 、 “then()” 和 “await Future.wait()”
1 | // ① 使用Future和Then级联同步调用 |
同上,await
是同步等待数据并执行,then
是异步侦听模式;而await Future.wait()
是多个异步并发执行,并等待所有异步操作执行返回。
下面使用Stopwatch来比较各个处理流程需要的时间。
1 | // ② |
根据以上的例子,②用的时间为2秒多些,③用的时间为1秒多些,③更好的并行使用了线程的时间。
因此如果业务场景适合,使用Future.wait会有更好的性能输出。
最后,一个例子展示如何使用FutureBuilder
1 | import 'dart:async'; |