Flutter 异步编程利器:Future 与 Stream 深度解析

目录

一、Future:处理单次异步操作

[1. 概念解读](#1. 概念解读)

[2. 使用场景](#2. 使用场景)

[3. 基本用法](#3. 基本用法)

[3.1 创建 Future](#3.1 创建 Future)

[3.2 使用 then 消费 Future](#3.2 使用 then 消费 Future)

[3.3 特性](#3.3 特性)

二、Stream:处理连续异步事件流

[1. 概念解读](#1. 概念解读)

[2. 使用场景](#2. 使用场景)

[3. 基本用法](#3. 基本用法)

[3.1 创建 Stream](#3.1 创建 Stream)

[3.2 监听 Stream](#3.2 监听 Stream)

[3.3 StreamSubscription 订阅者](#3.3 StreamSubscription 订阅者)

[3.4 Stream 广播模式](#3.4 Stream 广播模式)

[4. 特性](#4. 特性)

[三、Future 与 Stream 对比](#三、Future 与 Stream 对比)

四、高级技巧与最佳实践

[1. Future 的陷阱](#1. Future 的陷阱)

[2. Stream 的优化](#2. Stream 的优化)

五、async/await

[1. 使用 Future + then() 模式](#1. 使用 Future + then() 模式)

[2. 使用 async + await](#2. 使用 async + await)

[3. 回调地狱解决](#3. 回调地狱解决)

六、总结

相关推荐


一、Future:处理单次异步操作

在 Dart 库中随处可见 Future 对象,通常异步函数返回的对象就是一个 Future。 当一个 future 执行完后,他里面的值就可以使用了,可以使用 then() 来在 future 完成的时候执行其他代码。Future对象其实就代表了在事件队列中的一个事件的结果。

1. 概念解读

  • 定义Future 表示一个可能在未来完成的 单次异步操作,并返回一个值或错误(代表了事件结果)。

  • 状态

    • 未完成(Uncompleted):操作尚未结束。

    • 已完成(Completed):

      • 成功(value):可等待多个异步结果进行后续操作:wait方法。

      • 失败(error):对异步编程的异常捕获用 try/catch.catchError() 捕获异常。

2. 使用场景

  • 网络请求(如 http.get

  • 文件读写

  • 延迟任务(如 Future.delayed

  • 单次数据库查询

3. 基本用法

3.1 创建 Future

Dart 复制代码
  var dio = Dio();
  //通过 Dio 库发出HTTP GET请求返回的Future
  Future future= dio.get("https://www.wanandroid.com/banner/json");

3.2 使用 then 消费 Future

Dart 复制代码
void main() {
  var dio = Dio();
  //通过 Dio 库发出HTTP GET请求返回的Future
  Future future= dio.get("https://www.wanandroid.com/banner/json");
  //使用 then 消费 future 返回结果
  future.then((response){
    print("返回结果:$response");
  });
}

3.3 特性

  • 链式调用 :支持通过 .then() 串联多个异步操作。

  • 错误传播 :错误会沿着链式调用传递,直到被 catchError 捕获。

  • 嵌套地狱: 避免过度嵌套 .then(),优先使用 async/await(下面有讲)


二、Stream:处理连续异步事件流

Future 表示稍后获得的一个数据,所有异步的操作的返回值都用 Future 来表示。但是 Future 只能表示一次异步获得的数据。而 Stream 表示多次异步获得的数据。比如 IO 处理的时候,每次只会读取一部分数据和一次性读取整个文件的内容相比,Stream 的好处是处理过程中内存占用较小。而 Future 是一次性读取整个文件的内容进来,虽然获得完整内容处理起来比较方便,但是如果文件很大的话就会导致内存占用过大的问题。

1. 概念解读

  • 定义Stream 表示一个 连续的异步事件序列,可以发射多个值(数据、错误、完成信号)。

  • 数据流:类似于"管道",数据从生产者(如网络、传感器)流向消费者。

2. 使用场景

  • 实时聊天消息

  • 文件下载进度更新

  • 用户输入事件(如搜索框输入)

  • 传感器数据(如陀螺仪、GPS)

3. 基本用法

3.1 创建 Stream

Dart 复制代码
  //创建 Stream
  Stream<List<int>> stream = File("/Users/scc/Downloads/SCCDemo.apk").openRead();

3.2 监听 Stream

Dart 复制代码
  //订阅 Stream
  stream.listen((List<int> bytes) {
    print("SccFile----Stream执行"); //执行多次
  });

listen() 其实就是订阅这个Stream,它会返回一个 StreamSubscription 订阅者。订阅者提供了取消订阅的 cancel() 等方法,

3.3 StreamSubscription 订阅者

Dart 复制代码
  //创建 Stream
  Stream<List<int>> stream = File("/Users/scc/Downloads/SCCDemo.apk").openRead();
  //订阅 Stream
  StreamSubscription<List<int>> listen = stream.listen((List<int> bytes) {
    print("SccFile----Stream执行"); //执行多次
  });

  listen.onData((_){
    print("替代listen函数");
  });
  listen.onDone((){
    print("结束");
  });
  listen.onError((e,s){
    print("异常");
  });
  //暂停,如果没有继续则会退出程序
  listen.pause();
  //继续
  listen.resume();

3.4 Stream 广播模式

Stream有两种订阅模式:单订阅和多订阅。单订阅就是只能有一个订阅者,上面的使用我们都是单订阅模式,而广播是可以有多个订阅者。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

Dart 复制代码
  Stream<List<int>> stream = File("/Users/scc/Downloads/SCCDemo.apk").openRead();
  //换成一个多订阅模式的 Stream
  var broadcastStream = stream.asBroadcastStream();
  broadcastStream.listen((List<int> bytes) {
    print("SccFile----BroadcastStream执行111"); //执行多次
  });
  broadcastStream.listen((List<int> bytes) {
    print("SccFile----BroadcastStream执行222"); //执行多次
  });
  print("Stream模式:${broadcastStream.isBroadcast}");

4. 特性

  • 多值传递:可发射多个数据、错误或完成信号。

  • 冷热流(Cold/Hot Stream)

    • 冷流 :每次监听时开始生成数据(如上述 countNumbers)。

    • 热流:数据生成与监听无关(如用户点击事件)。

  • 操作符 :支持 mapwheredebounce 等操作符处理数据流。


三、Future 与 Stream 对比

特性 Future Stream
数据次数 单次结果 多次事件
适用场景 一次性异步操作(如 HTTP 请求) 连续事件流(如聊天、实时更新)
状态管理 只能完成一次 可持续发射数据或错误
错误处理 通过 catchErrortry/catch 通过 onErrorStreamBuilder
核心方法 then(), async/await listen(), async*, yield
内存占用 较低(单次操作) 较高(需维护订阅关系)

四、高级技巧与最佳实践

1. Future 的陷阱

  • 嵌套地狱 :避免过度嵌套 .then(),优先使用 async/await

  • 未处理的错误 :始终用 try/catch.catchError() 捕获异常。

  • 不必要的异步 :同步任务无需包装为 Future

2. Stream 的优化

  • 资源释放 :调用 subscription.cancel() 防止内存泄漏。

  • 防抖与节流 :使用 debouncethrottle 优化高频事件(如搜索输入)。

  • 广播流 :通过 .asBroadcastStream() 支持多个监听者。

五、async/await

使用 ``async + await`` 的代码是异步的,但是看起来很像同步代码。当我们需要获得A的结果,再执行B,时,你需要 ``then()->then(),合理利用 async + await 能够很好的解决回调地狱的问题。

下面是一个简单的网络请求,不同的实现方式,结果是相同的。

1. 使用 Future + then() 模式

Dart 复制代码
void main() {
  var dio = Dio();
  dio.get("https://www.wanandroid.com/banner/json").then((response) {
    print("返回结果:$response");
  });
}

2. 使用 async + await

Dart 复制代码
void main() async{
  var dio = Dio();
  Response response = await dio.get("https://www.wanandroid.com/banner/json");
  print("返回结果:$response");
}

3. 回调地狱解决

Dart 复制代码
import 'package:dio/dio.dart';

// void main() {
//   var dio = Dio();
//   dio.get("https://www.wanandroid.com/banner/json").then((response) {
//     print("返回结果:$response");
//     dio.get("https://www.wanandroid.com/article/list/1/json").then((s) {
//       print("返回结果:$s");
//     });
//   });
// }

void main() async{
  var dio = Dio();
  Response response = await dio.get("https://www.wanandroid.com/banner/json");
  Response response2 = await dio.get("https://www.wanandroid.com/article/list/1/json");
  print("返回结果:$response");
  print("返回结果:$response2");
}

当然如果你觉得这种方式写着不美观可借助 Future.wait 组合两个任务,在两个任务都完成后,再利用进行后面的操作。

Dart 复制代码
Iterable<Future> futures = [_getBanner(), _getArticlelist()];
await Future.wait(futures);

六、总结

  • Future 是处理 单次异步操作 的基石,适合简单、离散的任务。

  • Stream 是管理 连续事件流 的终极方案,适合实时性要求高的场景。

  • 选择依据

    • 需要单个结果? → 使用 Future

    • 需要持续更新? → 使用 Stream

掌握二者差异并合理运用,可显著提升 Flutter 应用的响应速度和代码可维护性。在实际开发中,常结合 Future 处理单次请求,用 Stream 管理状态(如 Bloc 库)或实时数据流,以实现高效异步编程。

相关推荐

Flutter Isolate入门指南:轻松实现高效并发编程-CSDN博客文章浏览阅读1k次,点赞30次,收藏30次。在Flutter开发中,面对复杂的业务逻辑和大量的数据处理需求,如何确保应用的流畅性和响应速度成为了开发者们关注的焦点。Flutter Isolate作为一种轻量级的并发执行单元,为我们提供了解决这一问题的有效手段。本文将带你深入了解Flutter Isolate的基本概念、使用场景以及如何在Flutter项目中轻松实现高效并发编程。_flutter isolatehttps://shuaici.blog.csdn.net/article/details/145505453Dart 中的Mixin:提高代码重用性和模块化的利器-CSDN博客文章浏览阅读1k次,点赞22次,收藏19次。本文介绍了Dart中Mixin的概念和使用方法。Mixin是一种代码重用机制,允许开发者将一些功能混入到一个类中,而不必通过继承来实现。文章详细阐述了Mixin的定义、使用以及与继承的冲突处理。通过使用Mixin,开发者可以大大提高代码的可重用性和模块化程度,将共通的功能封装起来,在需要的地方引入,避免了重复编写相同的代码。同时,Mixin还可以将复杂的代码逻辑拆分成更小的、可管理的模块,降低了代码的复杂性,提高了代码的可读性和可维护性。https://shuaici.blog.csdn.net/article/details/145332099

正在参与 2024 博客之星评选活动,希望大佬们多多支持,谢谢啦:
​​​​​​https://www.csdn.net/blogstar2024/detail/070

相关推荐
yan123684 小时前
Linux 驱动之设备树
android·linux·驱动开发·linux驱动
aningxiaoxixi6 小时前
android stdio 的布局属性
android
CYRUS STUDIO7 小时前
FART 自动化脱壳框架一些 bug 修复记录
android·bug·逆向·fart·脱壳
小蜜蜂嗡嗡7 小时前
flutter项目迁移空安全
javascript·安全·flutter
寻找优秀的自己8 小时前
Cocos 打包 APK 兼容环境表(Android API Level 10~15)
android·cocos2d
大胃粥9 小时前
WMS& SF& IMS: 焦点窗口更新框架
android
QING6189 小时前
Gradle 核心配置属性详解 - 新手指南(二)
android·前端·gradle
QING6189 小时前
Gradle 核心配置属性详解 - 新手指南(一)
android·前端·gradle
北极象10 小时前
在Flutter中定义全局对象(如$http)而不需要import
网络协议·flutter·http