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

相关推荐
web小白成长日记4 小时前
企业级 Vue3 + Element Plus 主题定制架构:从“能用”到“好用”的进阶之路
前端·架构
APIshop4 小时前
Python 爬虫获取 item_get_web —— 淘宝商品 SKU、详情图、券后价全流程解析
前端·爬虫·python
风送雨4 小时前
FastMCP 2.0 服务端开发教学文档(下)
服务器·前端·网络·人工智能·python·ai
XTTX1104 小时前
Vue3+Cesium教程(36)--动态设置降雨效果
前端·javascript·vue.js
LYFlied5 小时前
WebGPU与浏览器边缘智能:开启去中心化AI新纪元
前端·人工智能·大模型·去中心化·区块链
Setsuna_F_Seiei5 小时前
2025 年度总结:人生重要阶段的一年
前端·程序员·年终总结
model20055 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
han_6 小时前
从一道前端面试题,谈 JS 对象存储特点和运算符执行顺序
前端·javascript·面试
aPurpleBerry6 小时前
React 01 目录结构、tsx 语法
前端·react.js
jayaccc7 小时前
微前端架构实战全解析
前端·架构