题目
实现一个带错误处理和超时控制的异步数据流处理器
信息
- 类型:问答
- 难度:⭐⭐
考点
Future链式调用,Stream处理,错误处理,超时控制
快速回答
实现一个异步处理器,要求:
- 从两个异步数据源获取数据(模拟API)
- 合并数据后转换格式
- 添加超时控制(2秒)
- 实现错误处理机制
核心代码要点:
- 使用
Future.wait并行获取数据 - 用
async/await处理异步流程 - 通过
timeout设置超时 - 使用
try-catch捕获异常 - 用
Stream.fromIterable输出结果
问题场景
需要从两个异步数据源获取用户数据和订单数据,合并后转换为指定格式,要求:
- 并行获取数据提高效率
- 整个流程超时时间为2秒
- 优雅处理可能出现的错误
- 结果通过Stream输出
解决方案
Stream<Map<String, dynamic>> processUserData() async* {
try {
// 并行获取数据
final results = await Future.wait([
_fetchUserData(),
_fetchOrderData()
], eagerError: true).timeout(
const Duration(seconds: 2),
onTimeout: () => throw TimeoutException('Processing timed out')
);
// 数据转换
final user = results[0] as Map<String, dynamic>;
final orders = results[1] as List<dynamic>;
// 格式转换并输出
yield {
'userId': user['id'],
'userName': user['name'],
'orderCount': orders.length,
'lastOrder': orders.isNotEmpty ? orders.last : null
};
}
catch (e) {
// 统一错误处理
yield {'error': e.toString()};
}
}
// 模拟API请求
Future<Map<String, dynamic>> _fetchUserData() async {
await Future.delayed(Duration(milliseconds: 1500));
return {'id': 'U123', 'name': 'John Doe'};
}
Future<List<dynamic>> _fetchOrderData() async {
await Future.delayed(Duration(milliseconds: 800));
return [
{'id': 'O789', 'total': 49.99},
{'id': 'O002', 'total': 149.99}
];
}核心原理
- Future.wait:并行执行多个Future,
eagerError=true确保立即抛出首个错误 - timeout:限制整个异步操作的执行时间,超时抛出
TimeoutException - async* 和 yield:创建异步数据流,逐步输出结果
- 统一错误处理:catch块捕获所有异常(包括超时和API错误)并转换为数据格式
最佳实践
- 优先使用
async/await代替回调嵌套,提高可读性 - 为并行操作设置
eagerError:true避免不必要的等待 - 超时时间应略大于正常请求耗时(示例中总耗时1.5秒,超时2秒)
- 错误信息转换为结构化数据,保持输出流一致性
常见错误
| 错误示例 | 问题分析 | 修正方案 |
|---|---|---|
await _fetchUserData(); await _fetchOrderData(); | 顺序执行降低效率 | 改用Future.wait并行 |
缺少timeout | 可能永久阻塞 | 始终为网络请求添加超时 |
未处理onTimeout | 超时返回null导致后续崩溃 | 明确抛出异常 |
扩展知识
- StreamController:更复杂的流控制场景可使用
StreamController手动添加事件 - 错误隔离:使用
Future.catchError为单个Future添加专属错误处理 - 重试机制:通过
retry包或递归实现自动重试 - 测试技巧:使用
FakeAsync模拟时间流逝,验证超时逻辑