处理 Stream 的方法(返回结果)
Stream流的新建原理(_EmptyStream)
const factory Stream.empty() = _EmptyStream<T>;
工厂构造函数,用于创建一个空的广播流。这个流不包含任何数据,它的唯一作用是在被监听(subscribed)时发送一个 "done" 事件,表示流结束。
_EmptyStream
/// An empty broadcast stream, sending a done event as soon as possible.
class _EmptyStream<T> extends Stream<T> {
const _EmptyStream();
bool get isBroadcast => true;
StreamSubscription<T> listen(void onData(T data)?,
{Function? onError, void onDone()?, bool? cancelOnError}) {
return new _DoneStreamSubscription<T>(onDone);
}
}
StreamSubscription<T> listen(void onData(T data)?, {Function? onError, void onDone()?, bool? cancelOnError}):这是用于订阅(监听)流的方法。它接受一系列的回调函数,如onData用于处理流中的数据,onError用于处理错误,onDone用于在流完成时执行,cancelOnError用于指定是否在发生错误时自动取消订阅。在这个_EmptyStream类中,listen方法直接返回了一个_DoneStreamSubscription<T>对象,它会发送一个 "done" 事件,表示流已完成。
_EmptyStream 类,该类代表一个空的广播流。这个流没有实际的数据,只会立即发送一个 "done" 事件。当有监听者订阅这个流时,它会立即完成。这对于创建一个空的流并满足广播流的要求非常有用。
_DoneStreamSubscription
static const int _DONE_SENT = 1;
static const int _SCHEDULED = 2;
static const int _PAUSED = 4;
final Zone _zone;
int _state = 0;
void Function()? _onDone;
_DoneStreamSubscription(this._onDone) : _zone = Zone.current {
_schedule();
}
-
static const int _DONE_SENT = 1;、static const int _SCHEDULED = 2;、static const int _PAUSED = 4;:这些是整数常量,表示_DoneStreamSubscription的内部状态。它们用于跟踪订阅的状态,如是否已发送 "done" 事件、是否已安排发送事件、是否已暂停。 -
final Zone _zone;:这是一个不可变的_zone属性,它存储了当前的 Dart 执行区域(Zone)。 -
int _state = 0;:这是一个整数属性_state,用于跟踪订阅的状态。初始值为 0,表示订阅处于初始状态。 -
void Function()? _onDone;:这是一个可空的函数回调属性_onDone,用于存储当 "done" 事件触发时要执行的函数。 -
_DoneStreamSubscription(this._onDone) : _zone = Zone.current:这是_DoneStreamSubscription类的构造函数。它接受一个_onDone回调函数作为参数,并将当前 Dart 执行区域(Zone)存储在_zone属性中。构造函数的主要目的是在创建订阅时立即安排发送 "done" 事件,以完成订阅。_schedule()方法用于安排发送 "done" 事件,即将_state属性的状态设置为_SCHEDULED,并在当前 Zone 中执行_sendDone函数。
这个 _DoneStreamSubscription 类看起来是用于创建一个已完成的流订阅,当订阅被创建时,它会立即安排发送 "done" 事件,以通知订阅已完成。
bool get _isSent => (_state & _DONE_SENT) != 0;
bool get _isScheduled => (_state & _SCHEDULED) != 0;
bool get isPaused => _state >= _PAUSED;
它们用于检查 _DoneStreamSubscription 的内部状态:
bool get _isSent => (_state & _DONE_SENT) != 0;:这个属性用于检查_state是否包含_DONE_SENT标志位。如果_state中的_DONE_SENT位被设置,意味着 "done" 事件已经发送,此属性将返回true,否则返回false。bool get _isScheduled => (_state & _SCHEDULED) != 0;:这个属性用于检查_state是否包含_SCHEDULED标志位。如果_state中的_SCHEDULED位被设置,表示已经安排了发送 "done" 事件,此属性将返回true,否则返回false。bool get isPaused => _state >= _PAUSED;:这个属性用于检查_state是否大于或等于_PAUSED。如果_state大于或等于_PAUSED,表示订阅处于暂停状态,此属性将返回true,否则返回false。
_schedule
void _schedule() {
if (_isScheduled) return;
_zone.scheduleMicrotask(_sendDone);
_state |= _SCHEDULED;
}
该方法用于安排一个微任务 _sendDone 的执行,并更新了 _state 的状态以表示已经安排了任务。
具体来说:
_isScheduled属性用于检查是否已经安排了任务。如果_isScheduled返回true,则表示已经安排了任务,此时直接返回,不再重复安排任务。_zone.scheduleMicrotask(_sendDone)表示使用当前的 Dart Zone(通常是一个执行上下文,用于管理任务的执行)来安排一个微任务,该微任务执行_sendDone方法。微任务是一种异步任务,会在事件循环的下一个微任务队列中执行。- 最后,
_state |= _SCHEDULED将_SCHEDULED标志位设置为1,表示已经安排了任务。
通过这个 _schedule 方法,订阅在构造函数中创建时会被立即安排一个微任务来执行 _sendDone 方法,从而在订阅创建时发送 "done" 事件。这确保了 "done" 事件会尽快被发送,而不会等待下一个事件循环迭代。
onData、onError、onDone、pause、resume、cancel、asFuture
void onData(void handleData(T data)?) {}
void onError(Function? handleError) {}
void onDone(void handleDone()?) {
_onDone = handleDone;
}
void pause([Future<void>? resumeSignal]) {
_state += _PAUSED;
if (resumeSignal != null) resumeSignal.whenComplete(resume);
}
void resume() {
if (isPaused) {
_state -= _PAUSED;
if (!isPaused && !_isSent) {
_schedule();
}
}
}
Future cancel() => Future._nullFuture;
Future<E> asFuture<E>([E? futureValue]) {
E resultValue;
if (futureValue == null) {
if (!typeAcceptsNull<E>()) {
throw ArgumentError.notNull("futureValue");
}
resultValue = futureValue as dynamic;
} else {
resultValue = futureValue;
}
_Future<E> result = new _Future<E>();
_onDone = () {
result._completeWithValue(resultValue);
};
return result;
}
StreamSubscription 接口的实现,它用于订阅一个流(Stream)。
onData: 这是一个回调函数,用于处理从流中接收到的数据。它接受一个参数handleData,这个参数是一个函数,当数据到达时会被调用。但是在这个实现中,该方法没有实际的逻辑,因为它没有使用到handleData函数。onError: 同样是一个回调函数,用于处理从流中接收到的错误。它接受一个参数handleError,这个参数是一个函数,当出现错误时会被调用。但是在这个实现中,该方法也没有实际的逻辑,因为它没有使用到handleError函数。onDone: 这是一个回调函数,用于处理流的结束事件("done" 事件)。它接受一个参数handleDone,这个参数是一个函数,当流结束时会被调用。在这个实现中,_onDone成员变量被赋值为handleDone,表示当流结束时将调用这个函数。pause: 用于暂停订阅。当调用pause方法时,_state的状态会被更新,表示订阅已被暂停。如果提供了resumeSignal参数,那么在resumeSignal完成后,会自动调用resume方法来恢复订阅。resume: 用于恢复订阅。当调用resume方法时,会检查当前订阅是否已经暂停(isPaused),如果是,则更新_state的状态来表示订阅已经恢复,并且如果没有在暂停状态且还没有发送 "done" 事件(_isSent为假),则会调用_schedule方法来安排发送 "done" 事件。cancel: 用于取消订阅。这个方法返回一个Future,在这个实现中,它返回了一个完成状态为null的Future,表示取消操作已经完成。但是实际上,这个方法并没有执行取消订阅的操作,因为在这个具体的实现中,取消操作没有实际的逻辑。asFuture: 用于将订阅转化为一个Future。这个方法接受一个类型参数E,以及一个可选的futureValue参数。它返回一个Future,在_onDone回调触发时,会使用resultValue来完成这个Future。如果futureValue为null,则使用默认值futureValue,否则使用传入的futureValue。这个方法的目的是将订阅的结束事件映射为一个Future,以便可以轻松地等待订阅的结束事件。
_sendDone
void _sendDone() {
_state &= ~_SCHEDULED;
if (isPaused) return;
_state |= _DONE_SENT;
var doneHandler = _onDone;
if (doneHandler != null) _zone.runGuarded(doneHandler);
}
StreamSubscription 接口中的 _sendDone 方法的实现。它的作用是发送 "done" 事件给订阅者。
让我来逐步解释这个方法的各个部分:
_state &= ~_SCHEDULED;: 这一行代码用于清除_state中与_SCHEDULED标志位相关的状态,表示不再安排 "done" 事件发送。if (isPaused) return;: 这一行代码检查订阅是否处于暂停状态,如果是暂停状态,就不会发送 "done" 事件。这是因为当订阅处于暂停状态时,它不应该接收到任何事件。_state |= _DONE_SENT;: 这一行代码设置_state的_DONE_SENT标志位,表示 "done" 事件已经发送。var doneHandler = _onDone;: 这一行代码将_onDone方法存储在doneHandler变量中,以便稍后调用。if (doneHandler != null) _zone.runGuarded(doneHandler);: 最后,如果doneHandler不为空(即存在 "done" 事件的处理程序),则使用_zone.runGuarded方法来安全地运行doneHandler。这确保了即使 "done" 事件处理程序引发异常,也不会导致整个程序崩溃。
这个方法的主要作用是在合适的时机发送 "done" 事件给订阅者,并在发送之前进行一些状态的管理和检查,以确保事件被正确处理。
新建一个空的流,程序运行后, 返回Done
const stream = Stream.empty();
stream.listen(
(value) {
throw "Unreachable";
},
onDone: () {
print('Done');
},
);
any
Future<bool> any(bool test(T element)) {}
final result = await Stream.periodic(const Duration(seconds: 1), (count) => count).take(15).any((element) => element >= 5);
print(result); // true
判断流中是否有任何元素满足给定的条件 test,如果有,则返回 true,否则返回 false。
firstWhere
Future<T> firstWhere(bool test(T element), {T orElse()?}) {}
var result = await Stream.fromIterable([1, 3, 4, 9, 12]).firstWhere((element) => element % 6 == 0, orElse: () => -1);
print(result); // 12
result = await Stream.fromIterable([1, 2, 3, 4, 5]).firstWhere((element) => element % 6 == 0, orElse: () => -1);
print(result); // -1
在流中查找第一个满足给定条件 test 的元素,如果找到则返回该元素,否则返回一个默认值(通过 orElse 参数指定),如果没有指定 orElse 参数,且没有找到满足条件的元素,则会抛出 IterableElementError.noElement() 错误。
lastWhere
Future<T> lastWhere(bool test(T element), {T orElse()?}) {}
var result = await Stream.fromIterable([1, 3, 4, 7, 12, 24, 32]).lastWhere((element) => element % 6 == 0, orElse: () => -1);
print(result); // 24
result = await Stream.fromIterable([1, 3, 4, 7, 12, 24, 32]).lastWhere((element) => element % 10 == 0, orElse: () => -1);
print(result); // -1
在流中查找最后一个满足给定条件 test 的元素,如果找到则返回该元素,否则返回一个默认值(通过 orElse 参数指定),如果没有指定 orElse 参数,且没有找到满足条件的元素,则会抛出 IterableElementError.noElement() 错误。
reduce
Future<T> reduce(T combine(T previous, T element)) {}
final result = await Stream.fromIterable([2, 6, 10, 8, 2]).reduce((previous, element) => previous + element);
print(result); // 28
在流中对元素进行累积操作,使用指定的 combine 函数来计算最终的结果。这个函数将按顺序处理流中的元素,将上一个计算结果和当前元素传递给 combine 函数,并将计算结果作为下一次计算的输入,最终返回一个 Future<T>,该 Future 在流结束后完成,包含累积的结果。
every
Future<bool> every(bool test(T element)) {}
final result =await Stream.periodic(const Duration(seconds: 1), (count) => count)
.take(15).every((x) => x <= 5);
print(result); // false
every 方法用于检查流中的所有元素是否都满足指定的测试条件,即测试函数 test。如果所有元素都满足条件,则返回的 Future 完成为 true,否则完成为 false。
singleWhere
Future<T> singleWhere(bool test(T element), {T orElse()?}) {}
var result = await Stream.fromIterable([1, 2, 3, 6, 9, 12])
.singleWhere((element) => element % 4 == 0, orElse: () => -1);
print(result); // 12
result = await Stream.fromIterable([2, 6, 8, 12, 24, 32])
.singleWhere((element) => element % 9 == 0, orElse: () => -1);
print(result); // -1
result = await Stream.fromIterable([2, 6, 8, 12, 24, 32])
.singleWhere((element) => element % 6 == 0, orElse: () => -1);
Throws.
singleWhere 方法用于查找流中满足指定测试条件的唯一元素。如果找到符合条件的元素并且只有一个满足条件的元素,它将返回该元素。如果没有或有多个满足条件的元素,将根据提供的 orElse 参数或抛出异常来处理。
drain
Future<E> drain<E>([E? futureValue]) {}
final result = await Stream.fromIterable([1, 2, 3]).drain(100);
print(result); // Outputs: 100.
drain 方法用于监听流的事件,但不对数据事件执行任何处理,直到流结束。一旦流结束,它将返回一个 Future,用于表示处理结束后的状态。
处理 Stream 的方法(返回新Stream)
map
final stream = Stream<int>.periodic(const Duration(seconds: 1), (count) => count)
.take(5);
final calculationStream = stream.map<String>((event) => 'Square: ${event * event}');
calculationStream.forEach(print);
Square: 0
Square: 1
Square: 4
Square: 9
Square: 16
Stream<S> map<S>(S convert(T event)) {
return new _MapStream<T, S>(this, convert);
}
map 方法用于创建一个新的流,该流的事件是原始流事件经过某个转换函数转换后的结果。
_MapStream
typedef T _Transformation<S, T>(S value);
/// A stream pipe that converts data events before passing them on.
class _MapStream<S, T> extends _ForwardingStream<S, T> {
final _Transformation<S, T> _transform;
_MapStream(Stream<S> source, T transform(S event))
: this._transform = transform,
super(source);
void _handleData(S inputEvent, _EventSink<T> sink) {
T outputEvent;
try {
outputEvent = _transform(inputEvent);
} catch (e, s) {
_addErrorWithReplacement(sink, e, s);
return;
}
sink._add(outputEvent);
}
}
它是一个流管道,用于在将数据事件传递之前对其进行转换。
typedef T _Transformation<S, T>(S value);:这是一个泛型函数类型别名,用于表示将类型S的值转换为类型T的转换函数。_MapStream<S, T> extends _ForwardingStream<S, T>:_MapStream类继承自_ForwardingStream,这意味着它是一个流的装饰器,可以在原始流上添加功能。final _Transformation<S, T> _transform;:这是一个私有字段,用于存储将应用于数据事件的转换函数。_MapStream(Stream<S> source, T transform(S event)):这是_MapStream的构造函数,它接受两个参数。source是原始流,transform是转换函数。构造函数将转换函数_transform初始化为传递的transform函数,并调用了父类_ForwardingStream的构造函数,将原始流传递给它。_handleData(S inputEvent, _EventSink<T> sink):这是一个私有方法,用于处理数据事件。它接受两个参数:inputEvent是原始流中的数据事件,sink是新流的事件接收器。在此方法中,它将输入事件inputEvent传递给_transform函数,将其转换为类型T的事件。如果转换失败(例如,抛出异常),则会通过_addErrorWithReplacement方法通知事件接收器,传递异常信息。
_MapStream 类的主要目的是在原始流事件传递到新流之前,通过应用 _transform 函数来对事件进行转换。
_ForwardingStream
/// A [Stream] that forwards subscriptions to another stream.
///
/// This stream implements [Stream], but forwards all subscriptions
/// to an underlying stream, and wraps the returned subscription to
/// modify the events on the way.
///
/// This class is intended for internal use only.
abstract class _ForwardingStream<S, T> extends Stream<T> {
final Stream<S> _source;
_ForwardingStream(this._source);
bool get isBroadcast => _source.isBroadcast;
StreamSubscription<T> listen(void onData(T value)?,
{Function? onError, void onDone()?, bool? cancelOnError}) {
return _createSubscription(onData, onError, onDone, cancelOnError ?? false);
}
StreamSubscription<T> _createSubscription(void onData(T data)?,
Function? onError, void onDone()?, bool cancelOnError) {
return new _ForwardingStreamSubscription<S, T>(
this, onData, onError, onDone, cancelOnError);
}
// Override the following methods in subclasses to change the behavior.
void _handleData(S data, _EventSink<T> sink);
void _handleError(Object error, StackTrace stackTrace, _EventSink<T> sink) {
sink._addError(error, stackTrace);
}
void _handleDone(_EventSink<T> sink) {
sink._close();
}
}
一个抽象类 _ForwardingStream<S, T>,它代表一个将订阅传递给另一个流的流。
abstract class _ForwardingStream<S, T> extends Stream<T>:这是一个抽象类,代表一个流的装饰器。它有两个泛型参数S和T,分别表示原始流的事件类型和新流的事件类型。final Stream<S> _source:这是一个私有字段,表示原始流,即将接收所有订阅的流。_ForwardingStream(this._source):这是_ForwardingStream的构造函数,它接受一个Stream<S>类型的参数_source,并将其存储在私有字段_source中。bool get isBroadcast => _source.isBroadcast:这是一个 getter 方法,用于获取新流是否是广播流。它通过查询原始流_source的isBroadcast属性来确定。StreamSubscription<T> listen(void onData(T value)?, {Function? onError, void onDone()?, bool? cancelOnError}):这是Stream类的标准方法,用于订阅流的事件。在_ForwardingStream中,它会调用_createSubscription方法创建一个新的事件订阅,并将订阅的回调函数和参数传递给_createSubscription方法。StreamSubscription<T> _createSubscription(void onData(T data)?, Function? onError, void onDone()?, bool cancelOnError):这是一个抽象方法,需要在子类中实现。它用于创建新的事件订阅。在_ForwardingStream中,它创建了一个_ForwardingStreamSubscription,该订阅将事件处理委托给_handleData、_handleError和_handleDone方法。void _handleData(S data, _EventSink<T> sink):这是一个抽象方法,需要在子类中实现。它用于处理原始流传递的数据事件,并将其转发给新流的事件接收器。子类需要实现此方法以定义如何处理数据事件。void _handleError(Object error, StackTrace stackTrace, _EventSink<T> sink):这是一个抽象方法,需要在子类中实现。它用于处理原始流传递的错误事件,并将其转发给新流的事件接收器。子类需要实现此方法以定义如何处理错误事件。void _handleDone(_EventSink<T> sink):这是一个抽象方法,需要在子类中实现。它用于处理原始流传递的完成事件,并将其转发给新流的事件接收器。子类需要实现此方法以定义如何处理完成事件。
_ForwardingStream 类是一个用于创建流装饰器的抽象类,它可以用于修改原始流的事件处理方式,以创建新的流。子类需要实现 _handleData、_handleError 和 _handleDone 方法,以定义新流的行为。
_source是原输入流。
流的转换, 原理上是新建了一个流。
skip
final stream = Stream<int>.periodic(const Duration(seconds: 1), (i) => i).skip(7);
stream.forEach(print); // Skips events 0, ..., 6. Outputs events: 7, ...
Stream<T> skip(int count) {
return new _SkipStream<T>(this, count);
}
创建一个新的流 _SkipStream<T>,该流会跳过原始流中的前 count 个事件。以下是代码的逐行解释:
Stream<T> skip(int count):这是一个方法签名,表示创建一个新的流,该流会跳过原始流中的前count个事件,并返回新的流。return new _SkipStream<T>(this, count);:在skip方法中,它创建了一个_SkipStream的实例,并传递两个参数:this和count。this表示调用skip方法的原始流,而count表示要跳过的事件数量。
这个 _SkipStream 类的目的是创建一个新的流,该流会忽略原始流中的前 count 个事件。它通过在订阅时跳过这些事件来实现这一行为。这样,订阅新的 _SkipStream 后,最初的 count 个事件将不会传递给订阅者。
where
final stream = Stream<int>.periodic(const Duration(seconds: 1), (count) => count)
.take(10);
final customStream = stream.where((event) => event > 3 && event <= 6);
customStream.listen(print); // Outputs event values: 4,5,6.
Stream<T> where(bool test(T event)) {
return new _WhereStream<T>(this, test);
}
一个基于时间间隔的周期性流 stream,该流每隔1秒生成一个事件,事件值为生成事件的次数,然后限制了生成的事件数量为10个。
接着,它创建了一个名为 customStream 的新流,这个流通过在原始 stream 上应用 where 操作,筛选出事件值大于3且小于等于6的事件。
最后,它调用 customStream.listen(print),订阅了 customStream,并将事件值打印到控制台。因为 customStream 筛选出的事件值只包括4、5和6,所以在 listen 中打印的事件值将会是 4、5 和 6。
where 方法,它是用来创建一个新的流,该流会在原始流的事件上应用给定的测试函数 test。只有测试函数返回 true 的事件才会传递给新的流,其他事件都会被过滤掉。在这里,test 函数检查事件是否大于3且小于等于6,因此 customStream 中只包含满足这个条件的事件。