一、Dart异步编程的核心概念

在Dart的世界里,异步编程就像是在餐厅点餐。你下单后不会干等着厨师做完菜,而是先去做其他事情,等菜好了服务员自然会通知你。Dart通过FutureStream实现了这种非阻塞式的编程模式。

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 注意事项

  1. 避免嵌套地狱:用await替代.then()的深层嵌套。
  2. 资源释放Stream使用后务必调用cancel()close()
  3. 错误处理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));
  }
}