异步编程:Stream
重点:
- Stream 提供异步数据序列
- 数据序列包括用户生成事件和从文件中读取的数据
- 你可以使用 await for 或者 listen()处理Stream
- Stream提供了响应错误的方法
- 有两种Stream:single subscription(单订阅者) 或者 broadcast(广播)。
Future和Stream类是Dart异步编程的核心。
Future表示一个无法立即完成的计算过程。普通函数返回结果,而异步函数返回Future。 这个Future最终会包含结果,当结果准备好时,Future会通知调用者。
异步函数:被 async标记的函数。 例如:Future lookUpVersion() async => '1.0.0'; 只能返回Future
Stream是异步事件序列。这就像是一个异步迭代器。不会立即获取值,而是在Stream准备好事件时,获取到迭代值。
Stream初步使用
一个简单的Stream使用过程,对Stream产生直观的印象。
- 创建Stream:可以通过async*和 yield ,当然还有其他方式
- 监听Stream:stream.listen(),返回一个StreamSubscription
- 结束Stream:事件全部提交onDone,或者手动StreamSubscription.cancel()
Demo地址 入口 main_stream.dart
stream1_simple_use.dart
//1.Stream创建
//创建的方式有多种,这里我们使用异步生成器(async*)
//周期性发送整数Stream,当使用yield时,向Stream提交事件(一个整数)
Stream<int> timedCounter(Duration interval, [int maxCount]) async* {
int i = 0;
while (true) {
await Future.delayed(interval);
yield i++;
if (i == maxCount) break;
}
}
main() {
Stream<int> stream = timedCounter(Duration(milliseconds: 200), 5);
//2.监听Stream
StreamSubscription<int> subscription =
stream.listen(_onData, onDone: _onDone);
//3.取消订阅,我们先注释掉
//_delayCancel(subscription, milliseconds: 500);
}
//处理Stream提交的数据
void _onData(int event) => print("$event");
//Stream所有事件提交完成的回调
void _onDone() => print("onDone");
//延迟取消
void _delayCancel(StreamSubscription<int> subscription, {int milliseconds}) {
Future.delayed(Duration(milliseconds: milliseconds))
.then((value) => subscription.cancel());
}
//main()执行结果
0
1
2
3
4
onDone
//如果执行了_delayCancel
0
1
创建Stream
创建方式有多种,列举有代表性的方式
使用异步生成器(async*)
上面的例子已经提到过
使用Future序列
stream2_create_stream_by_future.dart
mport 'dart:async';
//创建一个Future,延迟毫秒数,返回整数i
Future<int> createFuture(int delayMilliseconds, int i) {
return Future.delayed(Duration(milliseconds: delayMilliseconds))
.then((value) => i);
}
Stream<T> streamFromFutures<T>(Iterable<Future<T>> futures) async* {
for (var future in futures) {
//请求future 并返回future的结果给Stream
var result = await future;
yield result;
}
}
main() {
List<int> values = [0, 1, 2, 3, 4];
List<Future<int>> futures =
values.map((i) => createFuture(i * 200, i)).toList();
Stream<int> stream = streamFromFutures(futures);
stream.listen(_onData, onDone: _onDone);
}
void _onData(int event) => print("$event");
void _onDone() => print("onDone");
执行结果
//main()执行结果
0
1
2
3
4
onDone
使用StreamController
实际使用过程中,使用 async*来创建Stream的场景很少。async函数的数据源往往是固定的。很多情况下,我们需要处理多个数据源,又或者数据只是在需要发送的时机发送,此时可以使用StreamController。 stream3_create_stream_by_streamcontroller.dart
//可以看到,只要拥有了controller实例,就可以发送event了,相对来说是灵活的。
//StreamController的细节很多,我们这里不介绍它的使用。
main() {
var controller = StreamController<int>();
controller.stream.listen(_onData, onDone: _onDone);
controller.add(1);
controller.add(2);
controller.add(3);
controller.close();
}
void _onData(int event) => print("$event");
void _onDone() => print("onDone");
接收Stream事件
await 处理事件
stream4_receving_stream_by_await_for.dart
import 'dart:async';
//生成一个Stream<int>
Stream<int> countStream(int to) async* {
for (int i = 1; i < to; i++) yield i;
}
//求和(迭代处理Stream事件),因为Stream是异步的,所以返回一个Future<int>
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (var value in stream) {
print("$value");
sum += value;
}
return sum;
}
main() async {
//使用await,sumStream执行完,才会执行接下来的print
var sum = await sumStream(countStream(5));
print("sum:$sum");
}
输出结果
0
1
2
3
4
sum:10
listen 监听事件
上面的例子已经介绍了。
两种类型的Stream
通过上面对流的使用,现在来看这两种流。Single subscription Streams(单订阅者流) 或者 Broadcast Stream(广播流)
Single subscription Streams
- 事件序列构成了某个整体。事件必须按照正确的顺序送达,且不能有丢失。比如说文件读取。
- 流中的事件只能被接收一次,错过了某次事件,则剩余事件毫无意义。当开始监听时,形象点来说,数据是一块一块的形式提供出来。
Broadcast Stream
- 相对于单订阅者流,这种Stream,事件不需要是严格序列。可以容忍极少的丢失。随机性的,不定时性的。
- 相对于单订阅者流,这种Stream,订阅者可以有多个,同时响应事件。
代码示例 stream5_two_kind_stream.dart
import 'dart:async';
main() {
broadcastStreamsTest();
//singleSubscriptionStreamsTest();
}
void broadcastStreamsTest() {
//此时返回的是 _AsyncBroadcastStreamController,广播流
var _streamController = StreamController<int>.broadcast();
_streamController.stream.listen((event) => print("$event"));
_streamController.stream.listen((event) => print("$event"));
_streamController.sink.add(100);
_streamController.close();
}
void singleSubscriptionStreamsTest() {
//此时返回的是 _AsyncStreamController,单订阅者流
var _streamController = StreamController<int>();
_streamController.stream.listen((event) => print("$event"));
_streamController.stream.listen((event) => print("$event"));
_streamController.sink.add(100);
_streamController.close();
}
执行singleSubscriptionStreamsTest(),报错了。提醒我们流已经被监听了。
单订阅者流,只能有一个订阅者
Unhandled exception:
Bad state: Stream has already been listened to.
#0 _StreamController._subscribe (dart:async/stream_controller.dart:683:7)
#1 _ControllerStream._createSubscription (dart:async/stream_controller.dart:833:19)
#2 _StreamImpl.listen (dart:async/stream_impl.dart:475:9)
#3 singleSubscriptionStreamsTest (package:flutter_sample/stream/stream5_two_kind_stream.dart:21:28)
#4 main (package:flutter_sample/stream/stream5_two_kind_stream.dart:5:3)
Stream操作符
前面提到 Stream就像是一个异步迭代器。 相仿于同步的迭代器(list?map?),我们想要在迭代器中完成一些操作。比如返回最后一个值?返回>0的值?求和?又或者整数流变成字符串流? 我们可以用同步的思想去理解这些异步的操作。
Stream->Future
如果只需要一个结果,则返回一个Future. 大部分操作符,在 Iterable 类中都有类似的。
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();
我们挑几个写写Demo,再次感受下stream6_stream_to_future.dart
import 'dart:async';
//构建Stream
Stream<int> countStream(int to) async* {
for (int i = 0; i < to; i++) yield i;
}
//构建List
List<int> countList(int to) {
List<int> list = List();
for (int i = 0; i < to; i++) list.add(i);
return list;
}
main() {
reduce();
forEach();
}
//我们发现 区别就在于 Stream/Future加了await去执行。
//用同步的方式去写异步。剩下部分,Stream和List是一样的。
void reduce() async {
Stream<int> stream = countStream(5);
Future<int> future = stream.reduce((previous, element) => previous + element);
var futureSum = await future;
List<int> list = countList(5);
var listSum = list.reduce((value, element) => value + element);
print("reduce futureSum:$futureSum listSum:$listSum");
}
void forEach() async {
Stream<int> stream = countStream(5);
StringBuffer streamSb = new StringBuffer();
await stream.forEach((value) => streamSb.write("$value,"));
List<int> list = countList(5);
StringBuffer listSb = new StringBuffer();
list.forEach((value) => listSb.write("$value,"));
print("stream forEach:$streamSb list forEach:$listSb");
}
结果打印
reduce futureSum:10 listSum:10
stream forEach:0,1,2,3,4, list forEach:0,1,2,3,4,
Stream-> Stream
如果需要得到的仍然是一个序列,大概感受下
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);
我们写个Demo,再次感受下 stream6_stream_to_stream.dart
void map() async {
Stream<int> stream = countStream(5);
Stream<String> strStream = stream.map((event) => "index($event)");
StringBuffer streamSb = new StringBuffer();
await strStream.forEach((value) => streamSb.write("$value,"));
List<int> list = countList(5);
StringBuffer listSb = new StringBuffer();
var strList = list.map((e) => "index($e)").toList();
strList.forEach((element) => listSb.write("$element,"));
print("map stream:$streamSb list:$listSb");
}
结果打印
map
stream:index(0),index(1),index(2),index(3),index(4),
list:index(0),index(1),index(2),index(3),index(4),
Stream与Iterable
Stream 理解成 Asynchronus Iterable. 除了相似点之外,还有Stream特别的,比如说一下等等。
Future pipe(StreamConsumer<T> streamConsumer);
Future<E> drain<E>([E futureValue]);
Stream<T> handleError(Function onError, {bool test(error)});
Stream<T> timeout(Duration timeLimit,
{void Function(EventSink<T> sink) onTimeout});
Stream<S> transform<S>(StreamTransformer<T, S> streamTransformer);
StreamTransformer
transform() 函数
transform()函数是更通用版的“map”。通常map来看,值的转换是一一对应的。然后,往往我们需要获取多个输入值,才能产生一个输出值。此时StreamTransformer,就可以完成这个操作。
我们写一个例子.
场景
输入一个字母空格序列:O,n,e, ,W,o,r,l,d, ,O,n,e, ,D,r,e,a,m (注意 e和W之间有一个空格)
输出一个单词序列:One World One Dream
我们用StreamTransformer来实现 letterStream转换成 wordStream
stream8_stream_transformer.dart
//一个字母和空格流,空格分割的字母序列都是一个单词
Stream<String> buildLetterStream() async* {
String s = "One World One Dream";
for (int i = 0; i < s.length; i++) {
String letter = s.substring(i, i + 1);
yield letter;
}
}
main() async {
Stream<String> letterStream = buildLetterStream();
var wordSplitter = WordSplitter();
//字母组成单词。字母序列-->单词序列
Stream<String> wordStream = letterStream.transform(wordSplitter.transformer());
await wordStream.forEach((word) => print(word));
}
class WordSplitter {
StreamTransformer<String, String> _transformer;
//存储字母
StringBuffer _wordBuilder = new StringBuffer();
StreamTransformer transformer() {
_transformer = StreamTransformer<String, String>.fromHandlers(
handleData: _handleData, handleDone: _sinkWord);
return _transformer;
}
//One World One Dream 对输入的letter进行处理,并决定是否需要重新发送给新的Stream
void _handleData(String letter, EventSink<String> sink) {
//读到空格,则把累计的字母构成单词输出。
if (letter == " ") {
_sinkWord(sink);
} else {
//否则存储字母
_wordBuilder.write(letter);
}
}
//读完时,把剩余字母构成单词输出
void _sinkWord(EventSink<String> sink) {
if (_wordBuilder.isNotEmpty) {
sink.add(_wordBuilder.toString());
_wordBuilder.clear();
}
}
}
输出结果
One
World
One
Dream
总结
到这里,我们介绍了Stream的基本用法。
- Stream是Dart异步编程的基石
- async*,StreamController等都可以创建Stream
- await 处理Stream事件,listen监听Stream事件
- Stream有两种
- Single Subscription Stream 严格事件序列,只能有一个订阅者
- Broadcast Stream 非严格事件序列,可以有多个订阅者
- Stream转换
- Iterable操作符(map,forearch,reduce等等)
- 更强大的StreamTransformer