【Flutter】Dart:异步

server/2024/10/22 12:31:19/

在现代应用开发中,异步编程是不可或缺的部分,尤其是在开发用户界面、网络请求、文件操作等涉及长时间执行的操作时,异步能避免阻塞主线程,从而提升应用的响应速度和用户体验。在 Dart 中,异步编程主要依靠 FutureStream,此外,Dart 还支持 生成器函数 来简化某些异步操作的实现。

本教程将深入介绍 Dart 中的异步编程,涵盖 FutureStream 以及生成器函数的使用。

异步编程概述

在 Dart 中,异步编程的主要目标是避免阻塞主线程(UI 线程),让长时间运行的任务在后台执行,同时主线程可以继续响应用户操作。异步编程通过 FutureStream 来实现。

  • Future:表示一个可能在将来某个时间完成的异步操作,可以成功返回一个结果或抛出错误。
  • Stream:类似于 Future,但可以在一段时间内多次返回数据。
  • 生成器函数:一种简化异步数据生成的机制,通常与 async*yield 关键字结合使用。

Future详解

Future 是 Dart 异步编程的基础,它表示一个将在未来完成的计算。Future 有两种状态:未完成(pending)完成(completed)。当一个异步操作完成时,Future 要么返回结果(成功),要么抛出异常(失败)。

创建 Future

Future 可以通过多种方式创建,包括 Future.value()Future.error() 和异步函数 async 的返回值。

示例:使用 Future.value()

void main() {Future<String> future = Future.value('Hello, Dart Future!');future.then((value) => print(value));
}

示例:使用异步函数

Future<String> fetchData() async {// 模拟网络请求,延迟 2 秒返回结果await Future.delayed(Duration(seconds: 2));return 'Data fetched!';
}void main() async {print('Fetching data...');String data = await fetchData();print(data);
}

在上面的示例中,fetchData 是一个异步函数,返回一个 Future<String>,并通过 await 关键字等待 Future 完成。在调用该函数时,主线程不会被阻塞。

thencatchError 处理结果

使用 Future 时,结果通常通过 then() 方法处理,错误通过 catchError() 处理。then() 方法会在 Future 完成后执行回调。

示例:处理结果和错误

void main() {Future<int> future = Future(() {return 42;});future.then((value) {print('Future completed with value: $value');}).catchError((error) {print('An error occurred: $error');});
}

asyncawait

在 Dart 中,asyncawait 提供了一种简洁的语法来处理异步操作,使代码看起来更像同步代码。

示例:使用 asyncawait

Future<int> fetchNumber() async {await Future.delayed(Duration(seconds: 1));  // 模拟耗时操作return 100;
}void main() async {print('Start fetching number...');int number = await fetchNumber();print('Fetched number: $number');
}

通过 async 标记一个函数为异步,使用 await 等待 Future 完成。在上面的例子中,fetchNumber() 返回一个 Future<int>,主线程等待该 Future 完成后继续执行。

Stream详解

Future 不同,Stream 代表一个异步数据的序列,可以多次发出数据。在 Dart 中,Stream 是处理大量异步事件(如用户输入、文件操作、网络请求)的重要工具。

创建 Stream

Stream 主要有两种类型:单订阅流(single-subscription stream)广播流(broadcast stream)

  • 单订阅流:只能有一个监听器。
  • 广播流:可以有多个监听器,通常用于事件广播。

示例:单订阅流

void main() {Stream<int> stream = Stream.fromIterable([1, 2, 3, 4, 5]);stream.listen((value) {print('Stream value: $value');});
}

示例:广播流

void main() {Stream<int> stream = Stream<int>.periodic(Duration(seconds: 1), (count) => count).asBroadcastStream();stream.listen((value) {print('Listener 1: $value');});stream.listen((value) {print('Listener 2: $value');});
}

在广播流中,多个监听器可以同时监听同一个 Stream,在上面的例子中,两个监听器同时监听并处理同一个 Stream

监听 Stream

通过 listen() 方法可以订阅 Stream,并处理流中的数据。

示例:处理 Stream

Stream<int> countStream(int max) async* {for (int i = 1; i <= max; i++) {yield i;await Future.delayed(Duration(seconds: 1));  // 模拟延迟}
}void main() async {Stream<int> stream = countStream(5);await for (int value in stream) {print('Stream value: $value');}
}

在这个例子中,countStream() 是一个生成器函数,通过 yield 发出数据,并通过 await for 逐个读取 Stream 中的数据。

转换 Stream

Stream 提供了许多方法用于转换流中的数据,比如 map()where()reduce() 等。

示例:转换 Stream

void main() {Stream<int> stream = Stream.fromIterable([1, 2, 3, 4, 5]);stream.map((value) => value * 2).where((value) => value > 5).listen((value) {print('Transformed Stream value: $value');});
}

这个例子展示了如何使用 map()where() 对流中的数据进行转换和过滤。

生成器函数

生成器函数可以生成一系列异步数据,它们使用 async*yield 关键字。生成器函数返回一个 Stream,可以逐步生成多个数据项。

sync*async*

  • sync*:同步生成器,用于生成一系列同步数据。
  • async*:异步生成器,用于生成一系列异步数据。

示例:使用 sync*

Iterable<int> syncGenerator(int max) sync* {for (int i = 1; i <= max; i++) {yield i;}
}void main() {var generator = syncGenerator(5);for (var value in generator) {print('Generated value: $value');}
}

示例:使用 async*

Stream<int> asyncGenerator(int max) async* {for (int i = 1; i <= max; i++) {yield i;await Future.delayed(Duration(seconds: 1));}
}void main() async {await for (var value in asyncGenerator(5)) {print('Generated async value: $value');}
}

async* 生成器与 sync* 不同的是,它会返回 Stream,并且可以处理异步任务。上面的例子中,asyncGenerator() 生成异步数据,并通过 await for 逐步读取。

总结

Dart 中的异步编程通过 FutureStream 和生成器函数为开发者提供了强大的工具,帮助处理复杂的异步操作。Future 用于处理一次性的异步任务,Stream 适合处理一系列异步事件,而生成器函数则提供了简洁的异步数据生成机制。

通过合理使用异步编程模式,开发者可以编写出高效、响应迅速的 Flutter 应用。在实际开发中,掌握这些异步工具对于提升应用性能和用户体验至关重要。


http://www.ppmy.cn/server/133895.html

相关文章

RocketMQ | 源码分析 | 消息刷盘

一、前言 在上篇文章中&#xff0c;我们分析了消息是如何从 Broker 最终存储到MappedFile 内存缓冲区中的&#xff0c;但是此时消息存储的任务并没有完成&#xff0c;因为消息还没有刷盘&#xff0c;即存储到文件中&#xff0c;本篇我们就来看看RocketMQ是如何进行消息刷盘的。…

Blender快捷键alt+A对齐没有反应/无效的解决方案(备忘录03)

目录 MACHIN3下载地址&#xff1a; 如果altA无效&#xff0c;那么有几个情况 没有正确安装MACHIN3&#xff0c;或者安装的版本与Blender版本冲突。 AltA快捷键与其他软件的快捷键冲突。 &#xff08;本人最终解决方案&#xff09;没有打开对齐饼菜单 测试结果 AltA快捷键…

在 gRPC 中,客户端和服务端的 Protocol Buffers(Protobuf)生成的文件必须保持一致性,以确保通信正常。

在 gRPC 中&#xff0c;客户端和服务端的 Protocol Buffers&#xff08;Protobuf&#xff09;生成的文件必须保持一致性&#xff0c;以确保通信正常。 关键点 相同的 .proto 文件&#xff1a; 客户端和服务端应该使用相同的 .proto 文件定义服务和消息结构。这确保了双方对数据…

数据库相关操作

1. 创建数据库 首先&#xff0c;使用 CREATE DATABASE 语句来创建一个新的数据库。 CREATE DATABASE my_database; 2. 使用数据库 创建数据库后&#xff0c;使用 USE 语句切换到这个数据库。 USE my_database; 3. 创建表 接下来&#xff0c;在数据库中创建一张表。表中…

python画图| 对齐图名和标签

【1】引言 学习了很多python画图教程之后&#xff0c;我们会发现&#xff1a;一些最基本的设置往往对图形的表达具有至关重要的影响。 因此&#xff0c;我们暂时回过头来&#xff0c;对一些基础知识进行加强。 今天&#xff0c;就一起学习如何对齐图名和标签。 【2】官网教…

解锁Claude五大能力,带你使用更加强大的Claude

很多人都听说过这样一句话&#xff1a;"想要AI生成更好的答案&#xff0c;你必须提供清晰、有效的提示。" 这句话现在可谓是家喻户晓。 然而&#xff0c;即便知道这个道理&#xff0c;很多人仍然不知道该如何写出好的提示词。他们常常面对空白的输入框&#xff0c;…

大厂面试真题-说一下推和拉的模式以及常见的使用

Pull&#xff08;拉&#xff09;模式和Push&#xff08;推&#xff09;模式是消息传递中的两种基本机制&#xff0c;它们在消息中间件和注册中心中的应用广泛而多样。 Pull&#xff08;拉&#xff09;模式 Pull模式是一种消息消费模式&#xff0c;其中客户端主动从服务端拉取…

使用Verilog设计分频模块(2Hz)

在数字电路设计中&#xff0c;分频器是一种常见的电路&#xff0c;用于将一个高频的时钟信号分频到一个较低频率的时钟信号。本次将通过一个实际的例子&#xff0c;讲解如何使用Verilog语言设计一个分频器&#xff0c;将系统时钟信号分频到2Hz。 在数字电路系统的设计中&#x…