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

相关推荐
永远的个初学者10 小时前
图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
前端·vue.js·react.js
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ10 小时前
npm i / npm install 卡死不动解决方法
前端·npm·node.js
Kratzdisteln10 小时前
【Cursor _RubicsCube Diary 1】Node.js;npm;Vite
前端·npm·node.js
杰克尼11 小时前
vue_day04
前端·javascript·vue.js
明远湖之鱼11 小时前
浅入理解跨端渲染:从零实现 React DSL 跨端渲染机制
前端·react native·react.js
悟忧12 小时前
规避ProseMirror React渲染差异带来的BUG
前端
小皮虾12 小时前
小程序云开发有类似 uniCloud 云对象的方案吗?有的兄弟,有的!
前端·javascript·小程序·云开发
Android疑难杂症12 小时前
鸿蒙Notification Kit通知服务开发快速指南
android·前端·harmonyos
T___T12 小时前
全方位解释 JavaScript 执行机制(从底层到实战)
前端·面试
阳懿12 小时前
meta-llama-3-8B下载失败解决。
前端·javascript·html