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)。

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

  • 操作符 :支持 map、where、debounce 等操作符处理数据流。


三、Future 与 Stream 对比

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

四、高级技巧与最佳实践

1. Future 的陷阱

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

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

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

2. Stream 的优化

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

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

  • 广播流 :通过 .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

相关推荐
福兮说39 分钟前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
leobertlan3 小时前
痛苦系列 | DSP-02 频域切片:DTFT与DFT的探索
android·后端
陆断枫3 小时前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter
leobertlan4 小时前
痛苦系列 | DSP-01 从连续到离散:DSP基础与采样
android·后端
程序员老刘4 小时前
Flet 1.0 思路很好,可惜来晚了
flutter·ai编程·客户端
事圆则缓6 小时前
Android 视频压缩上传实战:从 content Uri、转码到断点续传
android
weixin_422201307 小时前
如何解决小程序图标点击热区小,落点不准问题?
前端·小程序·样式·点击热区·扩大
茶底世界之下7 小时前
时间线效果层,为什么不能只复用一个处理器?
ios·swift
ZZH_AI项目交付8 小时前
实时字幕负责快,本地录音负责不丢:一个 iOS 录音功能为什么需要两条链路
ios·ai编程
河北清兮网络科技8 小时前
直播APP商用开发深度解析:为什么模板系统无法支撑规模化直播平台
运维·网络·人工智能·小程序·短剧app