Flutter 中的异步事件处理之Stream使用(四)

前面的章节, 我们探究了StreamStreamController的源码, 了解了它们的实现。接下来, 我们一起探讨怎样使用它们。

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方法里拿到这些数据执行相加操作。这里使用了streamlisten的方法进行监听。

参考资料

异步编程:使用 stream

在 Dart 里使用 Stream

Stream<​T> class

相关推荐
Hyyy5 小时前
普通前端续命周报——第1周
前端·javascript
KaMeidebaby6 小时前
卡梅德生物技术快报|抗独特型抗体开发:半抗原检测技术瓶颈拆解,抗独特型抗体开发工程化实践
前端·数据库·人工智能·其他·百度·新浪微博
2501_940041746 小时前
纯前端创意交互:五款全新实用工具与视觉应用生成指南
前端·交互
刀法如飞6 小时前
《道德经》简单解说版-第 2 章:天下皆知美之为美
前端·后端·面试
发现一只大呆瓜8 小时前
超全 Vite 性能优化指南:网络、资源、预渲染三维落地方案
前端·面试·vite
IT_陈寒9 小时前
Vue的computed属性怎么突然不更新了?
前端·人工智能·后端
智商不够_熬夜来凑9 小时前
【Picker】单选多选
前端·javascript·vue.js
米饭不加菜9 小时前
Typora 原生流程图语法完全指南(Flowchart.js)
前端·javascript·流程图
scan7249 小时前
langgraphy条件边
前端·javascript·html
冰小忆10 小时前
类变量在继承场景下的初始化规则是怎样的?
java·前端·数据库