Menu
CoddyTech

Stream 基础

本课属于 Coddy Dart 学习旅程的“面向对象编程”单元。第 77 课,共 110 课。

Future 会返回单个值,而 Stream 会随时间返回一系列值。你可以把它想象成一根管道,它能发出多条数据,非常适合处理事件、实时更新或逐条处理数据。

你可以使用 Stream.fromIterable() 创建简单的流,也可以使用带有 async* 和 yield 的异步生成器函数:

Stream<int> countToThree() async* {
  yield 1;
  yield 2;
  yield 3;
}

Future<void> main() async {
  await for (var number in countToThree()) {
    print(number);
  }
}
// 输出:1, 2, 3

async* 关键字用于标记返回 Stream 的函数,而 yield 会发出每个值。要使用这个流,请使用 await for,它会在每个值到达时进行处理。

你也可以使用 listen() 方法监听流,该方法接受一个回调函数:

Stream<String> greetings() async* {
  yield 'Hello';
  yield 'Hi';
  yield 'Hey';
}

void main() {
  greetings().listen((message) {
    print(message);
  });
}

与 Future 的关键区别在于:Future 只会完成一次,并返回一个值;而 Stream 可以在完成前发出多个值。因此,Stream 非常适合处理持续产生的数据,例如用户交互、传感器读数或分块到达的数据。

challenge icon

挑战

简单

让我们使用流来构建一个倒计时器系统,随着时间推移发出多个值!你将创建一个类,生成一系列倒计时数字,了解流如何传递多个值,而不只是一个值。

你将把代码整理到两个文件中:

  • countdown.dart:创建一个生成倒计时流的 Countdown 类。这个类应包含:
    • 一个 start 字段(int),用于存储倒计时的起始数字
    • 一个接收起始数字的构造函数
    • 一个返回 Stream<int> 的 countDown() 方法。使用 async* 和 yield 发出从 start 到 0(包含 0)的数字
    • 一个返回 Stream<String> 的 countDownWithMessage() 方法。对于从 start 到 1 的每个数字,该方法都应按 'T-minus [number]' 的格式发出字符串,然后将 'Liftoff!' 作为最后一个值发出
  • main.dart:导入倒计时文件,并演示这两个流方法。你的 main 函数应为 async,并且:
    • 创建一个从 3 开始的 Countdown 实例
    • 打印 Number countdown:
    • 使用 await for 遍历 countDown(),并打印每个数字
    • 打印一个空行
    • 打印 Launch sequence:
    • 使用 await for 遍历 countDownWithMessage(),并打印每条消息

注意,每条 yield 语句都会通过流发出一个值,而 await for 会在每个值到达时对其进行处理。与只传递一个结果的 Future 不同,你的流会按顺序发出多个值!

预期输出:

Number countdown:
3
2
1
0

Launch sequence:
T-minus 3
T-minus 2
T-minus 1
Liftoff!

亲手试试

import 'countdown.dart';

Future<void> main() async {
  // TODO: 创建一个从 3 开始的 Countdown 实例
  
  // TODO: 打印 'Number countdown:'
  
  // TODO: 使用 await for 遍历 countDown(),并打印每个数字
  
  // TODO: 打印一个空行
  
  // TODO: 打印 'Launch sequence:'
  
  // TODO: 使用 await for 遍历 countDownWithMessage(),并打印每条消息
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Dart 在线编译器