前面的章节, 我们探究了Stream
、StreamController
的源码, 了解了它们的实现。接下来, 我们一起探讨怎样使用它们。
Stream
Stream 是一系列异步事件的序列。其类似于一个异步的 Iterable,不同的是当你向 Iterable 获取下一个事件时它会立即给你,但是 Stream 则不会立即给你而是在它准备好时告诉你。
async*
下面这段代码演示了如何使用async*
进行 1 到 10 的相加。
csharp
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
print("---迭代流,value:$value---");
sum += value;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
void main() async {
var stream = countStream(10);
var sum = await sumStream(stream);
print(sum); // 55
}
执行结果:
diff
---迭代流,value:1---
---迭代流,value:2---
---迭代流,value:3---
---迭代流,value:4---
---迭代流,value:5---
---迭代流,value:6---
---迭代流,value:7---
---迭代流,value:8---
---迭代流,value:9---
---迭代流,value:10---
55
打印结果可以发现, yield
关键字标识发送一个流, await for
关键字标识不停地在接收流。
那么, 实际开发过程中, Stream 完成前会出现错误, 我们该怎样处理呢?
流错误事件
Stream 可以像提供数据事件那样提供错误事件。大多数 Stream 会在第一次错误出现后停止,但其也可以提供多次错误并可以在在出现错误后继续提供数据事件。我们只讨论 Stream 最多出现并提供一次错误事件的情况。
csharp
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
try {
await for (final value in stream) {
sum += value;
}
} catch (e) {
return -1;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
if (i == 5) {
throw Exception('Intentional exception');
} else {
yield i;
}
}
}
void main() async {
var stream = countStream(10);
var sum = await sumStream(stream);
print(sum); // -1
}
当使用 await for 读取 Stream 时,如果出现错误,则由循环语句抛出,同时循环结束。你可以使用 try-catch 语句捕获错误。下面的示例会在循环迭代到参数值等于 5 时抛出一个错误。
StreamController创建Stream
日常开发中,通常会通过StreamController
创建Stream
。只需要构造出StreamController
对象,通过这个对象的.stream
就可以得到Stream
。
csharp
Stream<int> countStream(int to) {
// 先创建 StreamController
late StreamController<int> controller;
controller = StreamController<int>(onListen: () {
// 当 Stream 被监听时会触发 onListen 回调
for (var i = 0; i < to; i++) {
controller.add(i);
}
controller.close();
});
return controller.stream;
}
Future<int> listenOn(Stream<int> stream) async {
var completer = Completer<int>();
var sum = 0;
// 监听 stream
stream.listen(
(event) {
sum += event;
},
onDone: () => completer.complete(sum),
);
return completer.future;
}
void main() async {
var stream = countStream(10);
// 当注释掉下面这行,控制台也不会打印出 "stream 被监听"
var sum = await listenOn(stream);
print(sum); // 55
}
在创建StreamController
的时候传入了一个onListen
回调,当流第一次被监听的时候,会触发这个回调,此时会往流里面依次添加多个数据,listenOn
方法里拿到这些数据执行相加操作。这里使用了stream
的listen
的方法进行监听。