一、Dart异步编程的核心概念
在Dart的世界里,异步编程就像是在餐厅点餐。你下单后不会干等着厨师做完菜,而是先去做其他事情,等菜好了服务员自然会通知你。Dart通过Future和Stream实现了这种非阻塞式的编程模式。
1.1 Future:一次性的异步操作
Future代表一个可能在未来完成的操作,比如网络请求或文件读取。它有三种状态:未完成、完成(成功)或完成(失败)。
// 示例:模拟网络请求(技术栈:Dart)
Future<String> fetchUserData() async {
await Future.delayed(Duration(seconds: 2)); // 模拟网络延迟
return '{"name": "小明", "age": 25}'; // 返回JSON字符串
}
void main() async {
print('开始请求数据...');
final data = await fetchUserData(); // 等待Future完成
print('收到数据: $data'); // 输出:收到数据: {"name": "小明", "age": 25}
}
注释:await会暂停当前函数执行,直到Future完成,但不会阻塞整个程序。
1.2 Stream:连续的数据流
Stream适用于持续产生数据的场景,比如聊天消息或传感器数据。它通过listen订阅数据流,通过StreamController控制数据推送。
// 示例:模拟实时温度传感器(技术栈:Dart)
import 'dart:async';
void main() {
final controller = StreamController<int>(); // 创建Stream控制器
// 模拟每秒推送一次温度数据
Timer.periodic(Duration(seconds: 1), (timer) {
controller.add(DateTime.now().second % 30 + 10); // 生成10~39的随机数
});
// 订阅数据流
final subscription = controller.stream.listen((temperature) {
print('当前温度: ${temperature}℃');
});
// 10秒后停止订阅
Timer(Duration(seconds: 10), () {
subscription.cancel();
controller.close(); // 关闭Stream
});
}
注释:StreamController是数据源,listen会持续接收数据直到取消订阅。
二、Future与Stream的应用场景
2.1 Future的典型场景
- 网络请求:如
http.get()返回一个Future<Response>。 - 文件读写:Dart的
File类方法(如readAsString())返回Future。 - 数据库操作:如
sqflite插件的查询操作。
// 示例:并发执行多个Future(技术栈:Dart)
Future<void> loadMultipleData() async {
final futures = [
fetchUserData(),
fetchProductList(),
fetchWeatherInfo(),
];
final results = await Future.wait(futures); // 并行执行所有Future
print('所有数据加载完成: $results');
}
2.2 Stream的典型场景
- 实时通信:WebSocket或Firebase的实时数据库。
- 用户交互:如按钮点击事件流(
onClick)。 - 状态管理:BLoC模式中用
Stream传递状态变化。
// 示例:合并多个Stream(技术栈:Dart)
final stream1 = Stream.periodic(Duration(seconds: 1), (i) => 'A$i');
final stream2 = Stream.periodic(Duration(seconds: 2), (i) => 'B$i');
void main() {
StreamZip([stream1, stream2]).listen(print); // 输出:A0, B0, A1, A2, B1...
}
三、技术优缺点与注意事项
3.1 Future的优缺点
- 优点:语法简洁(
async/await),适合单次异步操作。 - 缺点:无法处理连续事件,错误处理需依赖
try-catch。
3.2 Stream的优缺点
- 优点:支持背压(Backpressure)和复杂变换(如
map/where)。 - 缺点:学习曲线陡峭,需手动管理订阅生命周期。
3.3 注意事项
- 避免嵌套地狱:用
await替代.then()的深层嵌套。 - 资源释放:
Stream使用后务必调用cancel()或close()。 - 错误处理:
Stream可通过onError捕获异常。
// 错误处理示例(技术栈:Dart)
fetchUserData().then((data) {
print(data);
}).catchError((error) {
print('请求失败: $error');
});
四、总结与最佳实践
- Future:适合“一锤子买卖”场景,如API调用。
- Stream:适合“细水长流”场景,如实时数据推送。
- 混合使用:例如先用
Future初始化,再用Stream推送更新。
// 最佳实践示例:Future转Stream(技术栈:Dart)
Stream<String> getUserUpdates() async* {
while (true) {
yield await fetchUserData(); // 每次yield返回最新数据
await Future.delayed(Duration(seconds: 5));
}
}
评论