在 Flutter 中有两种处理异步操作的方式 Future
和 Stream
,Future
用于处理单个异步操作,Stream
用来处理连续的异步操作。
通俗来说,Stream 就是事件流或者管道:基于事件流驱动设计代码,然后监听订阅事件,并针对事件变换处理响应
。
在iOS端,
Stream
类似于RxSwift
或者ReactiveCocoa
框架的编程思想,通过可观察的流实现异步编程,既然有可观察
,就存在消息发送
和消息订阅
。
Stream 详解
Stream 是一个抽象类,用于表示一序列异步数据的源。它是一种产生连续事件的方式,可以生成数据事件或者错误事件,以及流结束时的完成事件。
abstract class Stream<T> {
Stream();
}
Stream
分单订阅流和广播流。
-
单订阅流在发送完成事件之前只允许设置一个监听器,并且只有在流上设置监听器后才开始产生事件,取消监听器后将停止发送事件。即使取消了第一个监听器,也不允许在单订阅流上设置其他的监听器。
-
广播流则允许设置多个监听器,也可以在取消上一个监听器后再次添加新的监听器。
Stream
有同步流和异步流之分。
它们的区别在于
- 同步流会在执行 add,addError 或 close 方法时立即向流的监听器 StreamSubscription 发送事件,
- 而异步流总是在事件队列中的代码执行完成后在发送事件。
Stream 家族
StreamController
:
带有控制流方法的流。 可以向它的流发送数据,错误和完成事件,也可以检查数据流是否已暂停,是否有监听器。sync
参数决定这个流是同步流还是异步流。
abstract class StreamController<T> implements StreamSink<T> {
Stream<T> get stream;
/// ...
}
StreamController _streamController = StreamController(
onCancel: () {},
onListen: () {},
onPause: () {},
onResume: () {},
sync: false,
);
StreamSink
:
流事件的入口。提供add
,addError
,addStream
方法向流发送事件。
abstract class StreamSink<S> implements EventSink<S>, StreamConsumer<S> {
Future close();
/// ...
Future get done;
}
StreamSubscription
:
流的监听器。提供 cacenl、pause, resume 等方法管理。
abstract class StreamSubscription<T> {
/// ...
}
StreamSubscription subscription = StreamController().stream.listen(print);
subscription.onDone(() => print('done'));
StreamBuilder
:
使用流数据渲染 UI 界面的部件,使用StreamBuilder
后,UI界面的更新就不再需要使用setState
方法了。
StreamBuilder(
// 数据流
stream: stream,
// 初始数据
initialData: 'loading...',
builder: (context, AsyncSnapshot snapshot) {
// AsyncSnapshot 对象为数据快照,缓存了当前数据和状态
// snapshot.connectionState
// snapshot.data
if (snapshot.hasData) {
Map data = snapshot.data;
return Text(data),
}
return CircularProgressIndicator();
},
)
创建 Stream
在 Dart 有几种方式创建 Stream
- 1、从现有的生成一个新的流 Stream,使用
map
,where
,takeWhile
等方法。
// 整数流
Stream<int> intStream = StreamController<int>().stream;
// 偶数流
Stream<int> evenStream = intStream.where((int n) => n.isEven);
// 两倍流
Stream<int> doubleStream = intStream.map((int n) => n * 2);
// 数字大于 10 的流
Stream<int> biggerStream = intStream.takeWhile((int n) => n > 10);
- 2、使用 async* 函数。
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
Stream stream = countStream(10);
stream.listen(print);
该段代码的执行过程:
当运行countStream
的时候,会立即返回Stream
,但函数体并不会执行,一旦开始listen
监听数据流,函数体会开始执行。当执行到yield
关键字的时候,会将yield
声明的求值表达式的计算结果添加到Stream
数据流中。