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

目录

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

1. 概念解读

2. 使用场景

3. 基本用法

3.1 创建 Future

3.2 使用 then 消费 Future

3.3 特性

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

1. 概念解读

2. 使用场景

3. 基本用法

3.1 创建 Stream

3.2 监听 Stream

3.3 StreamSubscription 订阅者

 3.4 Stream 广播模式

4. 特性

三、Future 与 Stream 对比

四、高级技巧与最佳实践

1. Future 的陷阱

2. Stream 的优化

五、async/await

1. 使用 Future + then() 模式

2. 使用 async + await

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

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

3.2 使用 then 消费 Future

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

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

3.2 监听 Stream

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

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

3.3 StreamSubscription 订阅者

  //创建 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 所处的模式。

  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 对比

特性FutureStream
数据次数单次结果多次事件
适用场景一次性异步操作(如 HTTP 请求)连续事件流(如聊天、实时更新)
状态管理只能完成一次可持续发射数据或错误
错误处理通过 catchError 或 try/catch通过 onError 或 StreamBuilder
核心方法then()async/awaitlisten()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() 模式

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

2. 使用 async + await

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

3. 回调地狱解决

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 组合两个任务,在两个任务都完成后,再利用进行后面的操作。

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 isolate https://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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/969683.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Agents Go Deep 智能体深入探索

Agents Go Deep 智能体深入探索 核心事件 OpenAI发布了一款先进的智能体“深度研究”&#xff0c;它能借助网络搜索和推理生成研究报告。 最新进展 功能特性&#xff1a;该智能体依据数百个在线资源生成详细报告&#xff0c;目前仅支持文本输出&#xff0c;不过很快会增加对图…

STM32单片机芯片与内部85 RS232 RS485 UART ISP下载硬件选择 电路设计 IO分配

目录 一、UART 1、硬件选择 2、电路设计 3、IO分配 4、其他设计 二、RS232 1、硬件选择 2、电路设计 3、IO分配 4、其他设计 三、RS485 1、硬件选择 2、电路设计 3、IO分配 4、其他设计 四、ISP下载 一、UART 1、硬件选择 一般选择CH340完成STM32的IO电平与US…

期权帮 | 场外个股期权可以做吗,风险高吗?

锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 场外个股期权可以做吗&#xff0c;风险高吗? 场外个股期权&#xff0c;就是在正式的交易所之外进行交易的个股期权。 注&#xff1a;这里的“场外”指的是这类交易不在像沪深…

【DeepSeek】deepseek可视化部署

目录 1 -> 前文 2 -> 部署可视化界面 1 -> 前文 【DeepSeek】DeepSeek概述 | 本地部署deepseek 通过前文可以将deepseek部署到本地使用&#xff0c;可是每次都需要winR输入cmd调出命令行进入到命令模式&#xff0c;输入命令ollama run deepseek-r1:latest。体验很…

USART串口协议

USART串口协议 文章目录 USART串口协议1. 通信接口2.串口通信2.1硬件电路2.2电平标准2.3串口参数及时序&#xff08;软件部分&#xff09; 3.USART串口外设3.1串口外设3.2USART框图3.3USART基本结构3.4数据帧 4.输入电路4.1起始位侦测4.2数据采样 5.波特率发生器6.相关函数介绍…

2025 西湖论剑wp

web Rank-l 打开题目环境&#xff1a; 发现一个输入框&#xff0c;看一下他是用上面语言写的 发现是python&#xff0c;很容易想到ssti 密码随便输&#xff0c;发现没有回显 但是输入其他字符会报错 确定为ssti注入 开始构造payload&#xff0c; {{(lipsum|attr(‘global…

twisted实现MMORPG 游戏数据库操作封装设计与实现

在设计 MMORPG&#xff08;大规模多人在线角色扮演游戏&#xff09;时&#xff0c;数据库系统是游戏架构中至关重要的一部分。数据库不仅承担了游戏中各种数据&#xff08;如玩家数据、物品数据、游戏世界状态等&#xff09;的存储和管理任务&#xff0c;还必须高效地支持并发访…

PyCharm 批量替换

选择替换的内容 1. 打开全局替换窗口 有两种方式可以打开全局替换窗口&#xff1a; 快捷键方式&#xff1a; 在 Windows 或 Linux 系统下&#xff0c;按下 Ctrl Shift R。在 Mac 系统下&#xff0c;按下 Command Shift R。菜单操作方式&#xff1a;点击菜单栏中的 Edit&…

LabVIEW用户界面设计原则

在LabVIEW开发中&#xff0c;用户界面&#xff08;UI&#xff09;设计不仅仅是为了美观&#xff0c;它直接关系到用户的操作效率和体验。一个直观、简洁、易于使用的界面能够大大提升软件的可用性&#xff0c;尤其是在复杂的实验或工业应用中。设计良好的UI能够减少操作错误&am…

网络防御高级-第8章及之前综合作业

标准版 接口ip配置 r2 [r2]interface GigabitEthernet 0/0/0 [r2-GigabitEthernet0/0/0]ip address 13.0.0.3 24 [r2-GigabitEthernet0/0/0]interface GigabitEthernet 0/0/1 [r2-GigabitEthernet0/0/1]ip address 100.1.1.254 24 [r2-GigabitEthernet0/0/1]interface Gigab…

若依系统环境搭建记录

开源若依系统网上资料也很全的&#xff0c;本篇博文记录下自己搭建环境过程中遇到的一些问题。 配置Maven和编辑器选择 我懒得配置Eclipse了&#xff0c;直接用vscode作为编辑器&#xff0c;后面构建运行都用命令行。 配置数据库连接 按照mysql5.7按网上教程即可&#xff1…

C# 运算符

总目录 前言 在C#中&#xff0c;运算符是用于执行特定操作的符号。它们可以用于处理变量、常量或其他表达式。C# 提供了丰富的运算符集合&#xff0c;用于执行各种操作&#xff0c;如算术运算、逻辑判断、位操作等。了解这些运算符及其使用方式对于编写高效且功能强大的C#程序…

为AI聊天工具添加一个知识系统 之103 详细设计之44 自性三藏 之4 祖传代码 之2

本文要点 要点 前面的所有讨论都是为了给出我的设计项目&#xff08;为使用AI聊天工具的聊天者 开挂一个知识系统&#xff09; 的祖传代码 的完整设计&#xff0c;其中 的“槽”&#xff08;占位符变量&#xff09;的 库元&#xff08;宝性和自性creator -本俱 替换内容标准模…

wireshark网络抓包

由于图片和格式解析问题&#xff0c;可前往 阅读原文 到这里已经讲了两个抓包工具的使用了&#xff0c;大家应该对抓包不是很陌生了。而wireshark相对于fiddler和charles更加偏向于网络层面的抓包或者说是一个网络封包分析工具。使用对象更适合于网络相关人员(网络管理员/相关运…

深入理解Linux网络随笔(一):内核是如何接收网络包的(下篇)

3、接收网络数据 3.1.1硬中断处理 数据帧从网线到达网卡时候&#xff0c;首先到达网卡的接收队列&#xff0c;网卡会在初始化时分配给自己的RingBuffer中寻找可用内存位置&#xff0c;寻找成功后将数据帧DMA到网卡关联的内存里&#xff0c;DMA操作完成后&#xff0c;网卡会向…

新版电脑通过wepe安装系统

官方下载链接 WIN10下载 WIN11下载 微PE 启动盘制作 1&#xff1a;选择启动盘的设备 2&#xff1a;选择对应的U盘设备&#xff0c;点击安装就可以&#xff0c;建议大于8g 3&#xff1a;在上方链接下载需要安装的程序包&#xff0c;放入启动盘&#xff0c;按需 更新系统 …

蓝桥杯之KMP算法

算法思想 代码实现 int* getnext() {int* next new int[s2.size()];int j 0;//用来遍历子串int k -1;//子串中公共子串的长度next[0] -1;while (j < s2.size() - 1){if (k-1||s2[k] s2[j]){k;j;if (s2[k] s2[j]){next[j] next[k];}else{next[j] k;}}else{k next[k…

jsp页面跳转失败

今天解决一下jsp页面跳转失败的问题 在JavaWeb的学习过程中&#xff0c;编写了这样一段代码&#xff1a; <html> <body> <h2>Hello World!</h2><%--这里提交的路径&#xff0c;需要寻找到项目的路径--%> <%--${pageContext.request.context…

如何实现对 ELK 各组件的监控?试试 Metricbea

上一章基于 Filebeat 的日志收集使用Filebeat收集文件中的日志&#xff0c;而Metricbeat则是收集服务器存活性监测和系统指标的指标。 1. Filebeat和Metricbeat的区别 特性FilebeatHeartbeat作用收集和转发日志监测服务可用性数据来源服务器上的日志文件远程主机、API、服务主…

DeepSeek-VL2 环境配置与使用指南

DeepSeek-VL2 环境配置与使用指南 DeepSeek-VL2 是由 DeepSeek 公司开发的一种高性能视觉-语言模型&#xff08;VLM&#xff09;。它是 DeepSeek 系列多模态模型中的一个版本&#xff0c;专注于提升图像和文本之间的交互能力。 本文将详细介绍如何配置 DeepSeek-VL2 的运行环…