如何将可能抛出异常的Stream<Foo>转换为Stream<Result<Foo>>?
如何将可能抛出异常的Stream转换为Stream<Result>?
要实现这个需求,我们可以利用Dart Stream的错误处理机制,把原始流里的正常数据包装成Result.success,同时捕获所有异常并包装成Result.error。最简洁且灵活的方式是通过扩展方法+StreamTransformer实现,既能复用逻辑,又能覆盖所有异常场景。
实现方案
1. 定义扩展方法
给Stream添加一个扩展方法,统一处理流的转换逻辑:
extension StreamResultExtension<T> on Stream<T> { Stream<Result<T>> toResultStream() { return transform(StreamTransformer.fromHandlers( // 处理每个正常数据流元素 handleData: (data, sink) { try { // 若处理data时有潜在异常(比如解析、转换操作),可在此处添加逻辑 sink.add(Result.success(data)); } catch (e, stackTrace) { // 捕获元素处理过程中的异常,包装为Result.error sink.add(Result.error('处理数据失败: $e')); // 可选:打印栈信息用于调试 // print('Stack trace: $stackTrace'); } }, // 捕获Stream本身抛出的错误(比如流内部的异常) handleError: (error, stackTrace, sink) { sink.add(Result.error('流异常: $error')); // print('Stack trace: $stackTrace'); }, // 流结束时关闭sink handleDone: (sink) => sink.close(), )); } }
2. 核心逻辑说明
这个实现覆盖了两种异常场景:
- 元素处理异常:处理单个流元素(比如解析Foo对象)时抛出的异常,会被
try-catch捕获并转为Result.error。 - 流本身的异常:如果原始Stream内部直接抛出异常(比如
async*函数中throw),会被handleError捕获并转为Result.error。
3. 使用示例
假设我们有一个可能抛出异常的原始流:
class Foo { final String value; Foo(this.value); } // 模拟一个可能抛出异常的Stream Stream<Foo> createRiskyStream() async* { yield Foo("有效数据1"); // 模拟流内部抛出异常 throw Exception("网络请求失败"); yield Foo("永远不会到达的数据"); }
使用扩展方法转换并监听结果:
void main() { createRiskyStream().toResultStream().listen((result) { // 使用模式匹配处理Result类型 switch (result) { case ResultSuccess(data: final foo): print("成功接收: ${foo.value}"); case ResultError(message: final msg): print("错误接收: $msg"); } }); }
输出结果
成功接收: 有效数据1 错误接收: 流异常: Exception: 网络请求失败
可选优化
- 可以根据异常类型定制错误信息,比如区分
FormatException、SocketException等,返回更精准的错误描述。 - 若不需要处理单个元素的异常(原始流元素本身不会触发异常),可以简化为
map+handleError的组合:
extension StreamResultExtension<T> on Stream<T> { Stream<Result<T>> toResultStream() { return this .map(Result.success) .handleError((error, _) => Result.error(error.toString())); } }
内容的提问来源于stack exchange,提问作者Alex.F
相关产品推荐
相关产品推荐

