Stream 对象出现在整个 Dart API 中,表示数据序列。例如,按钮点击等 HTML 事件通过 stream 来传递。你也可以读取一个文件作为 stream。
使用异步 for 循环
有时你可以使用一个异步 for 循环 (await for) 来代替 Stream API。
考虑下面的函数。它使用 Stream 的 listen() 方法来订阅一个列表中的文件,传递一个函数字面量来搜索每一个文件或目录。
void main(List<String> arguments) { // ... FileSystemEntity.isDirectory(searchPath).then((isDir) { if (isDir) { final startingDir = Directory(searchPath); startingDir .list( recursive: argResults[recursive], followLinks: argResults[followLinks]) .listen((entity) { if (entity is File) { searchFile(entity, searchTerms); } }); } else { searchFile(File(searchPath), searchTerms); } }); }
使用 await 表达式,并包含一个异步 for 循环 (await for) 的等效代码看起来更像同步代码:
Future main(List<String> arguments) async { // ... if (await FileSystemEntity.isDirectory(searchPath)) { final startingDir = Directory(searchPath); await for (var entity in startingDir.list( recursive: argResults[recursive], followLinks: argResults[followLinks])) { if (entity is File) { searchFile(entity, searchTerms); } } } else { searchFile(File(searchPath), searchTerms); } }
重要:在使用 await for 之前,确保代码清晰并且你真的想要等待所有 stream 的结果。例如,你通常不应该在 DOM 事件监器听上使用 await for,因为 DOM 会不停的发送事件。如果你使用 await for 先后注册两个 DOM 事件监听器,第二种事件从不会被处理。
监听 stream 数据
要在每个值到来时获取它,要么使用 await for,要么使用 listen() 方法监听 stream:
// 使用 ID 获取一个按钮并添加事件处理 querySelector('#submitInfo').onClick.listen((e) { // 当按钮被点击时,该代码被执行 submitData(); });
在这个例子中,onClick 属性是 "submitInfo" 按钮提供的一个 Stream 对象。
如果你只关心一个事件,可以使用类似 first、last 或者 single 这样的属性获得它。要在处理事件前测试它,使用类似 firstWhere()**、lastWhere()** 或者 singleWhere() 的方法。
如果你在意一个事件的子集,你可以使用类似 skip()**、skipWhile()、take()** 、takeWhile() 以及 where() 这样的方法。
转换 stream 数据
经常地,你需要在使用 stream 的数据前改变它的格式。使用 transform() 方法来生成一个不同类型的 stream 数据:
var lines = inputStream .transform(utf8.decoder) .transform(LineSplitter());
这个例子使用了两个转换器。第一个使用了 utf8.decoder 来转换整数的 stream 为字符串的 stream。然后它使用了一个 LineSplitter 来转换字符串的 stream 为按行分割的 stream。这个转换器来自 dart:convert 库(参见 [dart:convert 章节](#dart:convert---解码和编码 JSON、UTF-8 和其他))。
处理错误和完成
你如何指定错误和完成处理代码取决于你使用的是异步 for 循环 (await for) 还是 Stream API。
如果你使用了一个异步 for 循环,那么使用 try-catch 来处理错误。异步 for 循环之后的代码将在 stream 关闭后执行。
Future readFileAwaitFor() async { var config = File('config.txt'); Stream<List<int>> inputStream = config.openRead(); var lines = inputStream .transform(utf8.decoder) .transform(LineSplitter()); try { await for (var line in lines) { print('Got ${line.length} characters from stream'); } print('file is now closed'); } catch (e) { print(e); } }
如果你使用 Stream API,那么通过注册一个 onError 监听器来处理错误。使用 onDone() 监听器在 stream 关闭后执行代码。
var config = File('config.txt'); Stream<List<int>> inputStream = config.openRead(); inputStream .transform(utf8.decoder) .transform(LineSplitter()) .listen((String line) { print('Got ${line.length} characters from stream'); }, onDone: () { print('file is now closed'); }, onError: (e) { print(e); });
更多信息
要了解更多 Future 和 Stream 在命令行应用中的用法,请参阅 dart:io 教程。也请参阅以下文章和教程: