Flutter 中的异步事件处理之Stream处理、转换(五)

处理 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,在这个实现中,它返回了一个完成状态为 nullFuture,表示取消操作已经完成。但是实际上,这个方法并没有执行取消订阅的操作,因为在这个具体的实现中,取消操作没有实际的逻辑。
  • asFuture: 用于将订阅转化为一个 Future。这个方法接受一个类型参数 E,以及一个可选的 futureValue 参数。它返回一个 Future,在 _onDone 回调触发时,会使用 resultValue 来完成这个 Future。如果 futureValuenull,则使用默认值 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" 事件给订阅者。

让我来逐步解释这个方法的各个部分:

  1. _state &= ~_SCHEDULED;: 这一行代码用于清除 _state 中与 _SCHEDULED 标志位相关的状态,表示不再安排 "done" 事件发送。
  2. if (isPaused) return;: 这一行代码检查订阅是否处于暂停状态,如果是暂停状态,就不会发送 "done" 事件。这是因为当订阅处于暂停状态时,它不应该接收到任何事件。
  3. _state |= _DONE_SENT;: 这一行代码设置 _state_DONE_SENT 标志位,表示 "done" 事件已经发送。
  4. var doneHandler = _onDone;: 这一行代码将 _onDone 方法存储在 doneHandler 变量中,以便稍后调用。
  5. 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');
   },
 );

参考资料

异步编程:使用 stream

相关推荐
冰冻果冻18 分钟前
vue--制作随意滑动的相册
前端·javascript·vue.js
GISer_Jing37 分钟前
前端测试工具(Jest与Mock)
前端·测试工具
licy__1 小时前
HTML 元素类型介绍
前端·css·html
一雨方知深秋1 小时前
WEB APIS(DOM对象,操作元素内容,属性,表单属性,自定义属性,定时器)
开发语言·前端·javascript
三金121381 小时前
整站使用Vue(工程化)
前端·javascript·vue.js
爪哇学长1 小时前
打造极致网页体验:HTML与CSS高级实战秘籍
前端·css·html
程序猴老王2 小时前
el-select 和el-tree二次封装
前端·vue.js·elementui
blzlh3 小时前
手把手教你做网易云H5页面,进大厂后干的第一件事
前端·javascript·css
贩卖纯净水.3 小时前
网站部署及CSS剩余模块
前端·css
Justinc.3 小时前
CSS3_BFC(十二)
前端·css·css3