处理 Stream 的方法(返回结果)
Stream流的新建原理(_EmptyStream)
arduino
const factory Stream.empty() = _EmptyStream<T>;
工厂构造函数,用于创建一个空的广播流。这个流不包含任何数据,它的唯一作用是在被监听(subscribed)时发送一个 "done" 事件,表示流结束。
_EmptyStream
csharp
/// 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
ini
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" 事件,以通知订阅已完成。
ini
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
javascript
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
ini
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
ini
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
ini
const stream = Stream.empty();
stream.listen(
(value) {
throw "Unreachable";
},
onDone: () {
print('Done');
},
);