Flutter学习 - Bloc - 02 流(Stream)

572 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

本文主要介绍Strema的概念以及Stream如何使用

为了使用 Bloc,对 Streams 及其工作方式有扎实的了解是十分必要的。如果对iOS中的RxSwift框架有所了解的话,序列不断发送事件。Dart中的Stream也是类似,类似一个管道发送一个个数据。

image.png

Dart中的异步编程特点是FutureStream,这里简单介绍下Future

1. Futre

在Dart中编程异步编程是FutureStream,对于Futrue平时使用的比较多

void main() {
  test();
  print('做其他事');
}
String _string = '0';

test() async{

  print('开始请求数据string:$_string');

 Future((){
   for (int i = 0;i<1000000000;i++){

     _string = '网络数据';

   }

   print('请求结束:string:$_string');
 });

}

执行结果:

开始请求数据string:0
做其他事
请求结束:string:网络数据

Future() 相当于一个对象()里面的任务放到异步任务里面了,里面的任务执行完成后会返回数据或者void。通常我们使用 awiat后面的操作必须是异步才能使用await修饰。这里就不介绍了,Future 表示不会立即完成的计算。在普通函数返回结果的地方,异步函数返回一个 Future,它最终将包含结果。当结果准备好时,未来会告诉你。

2. Stream创建

是一系列异步事件。它就像一个异步迭代器 —— 当你请求下一个事件时,流不会在你请求它时获取它,而是在它准备好时告诉你有一个事件。

举个例子:

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
    print('sum:$sum');
  }
  return sum;
}

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    print('countStream:$i');
    yield i;
  }
}

void testStream() async {
  var stream = countStream(10);
  var sum = await sumStream(stream);
  print(sum); // 55
}

yield为周围的async*函数的输出流添加一个值。它就像return,但不会终止该功能。

此代码仅接收整数事件流中的每个事件,将它们相加,并返回总和(的未来)。当循环体结束时,函数会暂停,直到下一个事件到达或流完成。

打印结果

image.png

可以发现2个循环就行流水一样不断传送,是一个异步事件,但是却又像同步的效果。

创建Stream的方式通常可以下面的方式:

  • 创建fromFuture
Stream的方式通常可以下面的方式:<int> stream = Stream<int>.fromFuture(Future(()=>100));
fromFutures
  • fromFutures
Stream<int> stream2 = Stream<int>.fromFutures([Future(()=>100),Future(()=>200)]);
  • periodic
final stream =
   Stream<int>.periodic(const Duration(
     seconds: 1), (count) => count * count).take(5);
stream.forEach(print);

3. 订阅

  • 单一订阅流

最常见的流类型包含一系列事件,这些事件是更大整体的一部分。事件需要以正确的顺序传递,并且不能遗漏任何一个。这是您在读取文件或接收 Web 请求时获得的流。

这样的流只能听一次。稍后再次收听可能意味着错过初始事件,然后流的其余部分没有意义。当您开始收听时,数据将被提取并以块的形式提供。

final stream =
   Stream<int>.periodic(const Duration(
     seconds: 1), (count) => count * count).take(5);
stream.forEach(print);

每隔一秒打印,执行5次,每次计数器乘以平方

image.png

  • 监听 要创建一个新Stream类型,您只需扩展Stream 类并实现listen()方法——所有其他方法都在Stream调用listen()才能工作。

listen()方法允许您开始收听流。在您这样做之前,流是一个惰性对象,描述您想要查看的事件。当您收听时,将返回一个StreamSubscription对象,该对象表示正在生成事件的活动流。这类似于 一个Iterable只是对象的集合,但迭代器是执行实际迭代的对象。

流订阅允许您暂停订阅、暂停后恢复订阅以及完全取消订阅。您可以设置为每个数据事件或错误事件以及流关闭时调用的回调。

stream.listen((event) {
  print(event);
},
  onError: (error) {
  print(error);
  },
  onDone: ()=> print('done'),
  cancelOnError: true

);

image.png

  • 广播流

另一种流旨在用于一次可以处理一个的单个消息。例如,这种流可用于浏览器中的鼠标事件。

您可以随时开始收听这样的流,并在收听时获得触发的事件多个听众可以同时收听,您可以在取消之前的订阅后再次收听。

广播则相当于可以被多个监听

final stream =
Stream<int>.periodic(const Duration(
    seconds: 1), (count) => count * count).take(5);
stream.listen((event) {
  print(event);
});
stream.listen((event) {
  print(event);
});
//报错[VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: Bad state: Stream has already been listened to.

我们使用asBroadcastStream转换

final stream =
Stream<int>.periodic(const Duration(
    seconds: 1), (count) => count * count).take(5).asBroadcastStream()
  • Stram的一些方法和属性
Future<T> get first;
Future<bool> get isEmpty;
Future<T> get last;
Future<int> get length;
Future<T> get single;
Future<bool> any(bool Function(T element) test);
Future<bool> contains(Object? needle);
Future<E> drain<E>([E? futureValue]);
Future<T> elementAt(int index);
Future<bool> every(bool Function(T element) test);
Future<T> firstWhere(bool Function(T element) test, {T Function()? orElse});
Future<S> fold<S>(S initialValue, S Function(S previous, T element) combine);
Future forEach(void Function(T element) action);
Future<String> join([String separator = '']);
Future<T> lastWhere(bool Function(T element) test, {T Function()? orElse});
Future pipe(StreamConsumer<T> streamConsumer);
Future<T> reduce(T Function(T previous, T element) combine);
Future<T> singleWhere(bool Function(T element) test, {T Function()? orElse});
Future<List<T>> toList();
Future<Set<T>> toSet();
  • 修改流的方法
Stream<R> cast<R>();
Stream<S> expand<S>(Iterable<S> Function(T element) convert);
Stream<S> map<S>(S Function(T event) convert);
Stream<T> skip(int count);
Stream<T> skipWhile(bool Function(T element) test);
Stream<T> take(int count);
Stream<T> takeWhile(bool Function(T element) test);
Stream<T> where(bool Function(T event) test);

Stream<E> asyncExpand<E>(Stream<E>? Function(T event) convert);
Stream<E> asyncMap<E>(FutureOr<E> Function(T event) convert);
Stream<T> distinct([bool Function(T previous, T next)? equals]);

4. 小结

Stream属于Dart中的异步操作,同时类似sink不断传输数据,我们可以进行监听,可以作为实现响应式的基础。