简介:Stream是一个异步的事件队列,
编程和核心就是处理数据,从上游拿到数据,经过处理后传递给下游。
后续随着需要处理的数据越来越多,有了集合的概念,包装了一系列数据在上下游之间进行传递。
随着计算资源的发展,业务数据的处理可能要跨多个线程,这样数据处理流程就从单个线程的同步处理变成跨多个线程的异步处理。此时集合通常充当多个线程之间协作的缓冲池,当缓冲池满了的时候上游线程阻塞,空了的时候下游线程阻塞。
在开发过程中经常需要开发人员写很多遍历数据集合的操作,就出现了集合的迭代器来简化遍历。在遍历过程中常会有筛选、排序、映射转换、合并或者拆分等操作。
流的出现就是简化针对集合的这些操作,方便开发。流关注的是针对数据源的一系列操作而不是数据的存储。
所以流就是一个数据源+管道(多个中间操作)+终端操作组成。
数据源:定义流操作的原始数据是哪里来的,可以是集合、数组、生成器函数、io管道以及其他流作为数据来源。
中间操作:针对流中的数据进行处理,处理产生的结果还是流:比如过滤filter、一对多flatMap、一对一的变换map、排序sorted等等操作。
终端操作:定义了流中元素最终要经历的操作,比如最大最小值、个数统计、转换成集合或其他类型、提供对元素遍历的接口等。终端操作的执行意味着定义的流开始消耗,等最后一个元素经历过终端操作后,这个流就相当于完成了。
流的中间操作是惰性的:中间操作并不会立即执行,而是等终端操作开始执行时才会被执行。
中间操作又被分为
(1)有状态的中间操作:比如排序、去重等操作,需要知道整个序列的所有元素才可以完成操作。
(2)无状态的中间操作:比如过滤、映射等操作,单个元素就可以完成操作,不需要知道其他元素的状态。所以这种中间操作是可以并行处理的。
短路操作:如果在提供无限流输入时,它可能会产生一个有限的流,那么他就是短路的。如果在无限流作为输入时,它可能在有限的时间内终止,这个终端操作是短路的。
流不存储数据,流的中间操作并不修改数据源的结构。流的短路操作让其可以处理无线数据源。流的延迟特性让其可以优化中间操作。
流的惰性让流可以处理异步事件。
大致了解了流的特性之后,我们开始看Dart中的流是如何使用的。
Future和Stream是Dart异步库中的核心,Future代表的是单个异步事件,Stream代表的是一个异步事件序列。
1.创建流
首先看Dart中是如何创建流的:
(1)通过生成器创建流
async*
标记的方法称为异步生成器,在其中可以通过yield
生成单个元素,yield*
生成多个元素,最终汇集成流。
Stream<String> createStream() async* {
for (int i = 0; i < 100; i++) {
await Future.delayed(Duration(seconds: 1));
yield "第${i}份数据";
}
}
main() {
Stream<String> result = createStream();
Future future = result.forEach((element) {
print(element);
});
print('future开始!');
}
(2)通过流命名构造函数创建流
根据已有的数据序列创建流序列。
Stream.fromIterable(Iterable<T> elements)
根据Future创建流
Stream.fromFuture(Future<T> future)
Stream.fromFutures(Iterable<Future<T>> futures)
(3)通过StreamController创建流
其实上面(2)中创建流的方式底层都是通过StreamController来创建的。
StreamController<int> controller = StreamController();
Stream<int> stream = controller.stream;
2. 流的订阅
StreamSubscription<T> listen(void onData(T event)?,{Function? onError, void onDone()?, bool? cancelOnError});
Stream可以通过listen方法来创建流的订阅,需要回调函数。返回的是订阅类StreamSubscription
;
通订阅流可以监听流中的数据或事件。
单订阅流
默认情况下创建的流都是单订阅流,单订阅流只能被订阅一次,第二次监听会报错!监听开始之前的元素不会被订阅。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。
广播订阅
广播流允许存在任意数量的 listener,并且无论是否存在 listener,它都能产生事件,所以中途加入的 listener 不会侦听到已发生的事件。
单订阅流常用于数据的传递,广播流用于事件的分发。
3.常用方法
4.流的结构
这里简单整理了Dart流中重要的类和接口。
(1)Sink
abstract class Sink<T> {
void add(T data);
void close();
}
Sink是流控制器最基础的接口,定义了一个通用的数据接收器所需要的方法,
(2)EventSink
abstract class EventSink<T> implements Sink<T> {
void add(T event);
void addError(Object error, [StackTrace? stackTrace]);
void close();
}
EventSink是对Sink的扩展,添加了处理Error的方法。
(3)StreamConsumer
abstract class StreamConsumer<S> {
Future addStream(Stream<S> stream);
Future close();
}
StreamConsumer 定义了可以接受流输入的槽
(4)StreamSink
abstract class StreamSink<S> implements EventSink<S>, StreamConsumer<S> {
Future close();
Future get done;
}
StreamSink定义了可以同步或异步接收流事件的接口方法。
(5)StreamController
abstract class StreamController<T> implements StreamSink<T> {
/** The stream that this controller is controlling. */
Stream<T> get stream;
void Function()? get onListen;
void set onListen(void onListenHandler()?);
void Function()? get onPause;
void set onPause(void onPauseHandler()?);
void Function()? get onResume;
void set onResume(void onResumeHandler()?);
FutureOr<void> Function()? get onCancel;
void set onCancel(FutureOr<void> onCancelHandler()?);
StreamSink<T> get sink;
bool get isClosed;
bool get isPaused;
bool get hasListener;
void add(T event);
void addError(Object error, [StackTrace? stackTrace]);
Future close();
Future get done;
Future addStream(Stream<T> source, {bool? cancelOnError});
}
StreamController 是流控制器的核心接口,包含了流控制器该有的大多数接口方法。
其中
stream用于向外提供创建的Stream。
sink 是该控制器关联的流的数据来源。可以使用sink.add 方法向流中添加数据。
onListen, 当控制器中的流被监听的时候,会回调该方法。
onPause, 当流的监听主动暂停的时候,会回调该方法。
onResume, 当流的监听主动恢复监听的时候,会回调该方法。
onCancel,当流的监听取消监听的时候,会回调该方法。
factory StreamController(
{void onListen()?, void onPause()?, void onResume()?,FutureOr<void> onCancel()?, bool sync = false}) {
return sync
? _SyncStreamController<T>(onListen, onPause, onResume, onCancel)
: _AsyncStreamController<T>(onListen, onPause, onResume, onCancel);
}
factory StreamController.broadcast(
{void onListen()?, void onCancel()?, bool sync = false}) {
return sync
? _SyncBroadcastStreamController<T>(onListen, onCancel)
: _AsyncBroadcastStreamController<T>(onListen, onCancel);
}
流控制器中有两个工厂构造函数,分别构造了单订阅,和广播流以及同步和异步流。
https://www.cnblogs.com/noteless/p/9505098.html#0