在现代应用开发中,异步编程是不可或缺的部分,尤其是在开发用户界面、网络请求、文件操作等涉及长时间执行的操作时,异步能避免阻塞主线程,从而提升应用的响应速度和用户体验。在 Dart 中,异步编程主要依靠 Future
和 Stream
,此外,Dart 还支持 生成器函数 来简化某些异步操作的实现。
本教程将深入介绍 Dart 中的异步编程,涵盖 Future
、Stream
以及生成器函数的使用。
异步编程概述
在 Dart 中,异步编程的主要目标是避免阻塞主线程(UI 线程),让长时间运行的任务在后台执行,同时主线程可以继续响应用户操作。异步编程通过 Future
和 Stream
来实现。
Future
:表示一个可能在将来某个时间完成的异步操作,可以成功返回一个结果或抛出错误。Stream
:类似于Future
,但可以在一段时间内多次返回数据。- 生成器函数 :一种简化异步数据生成的机制,通常与
async*
和yield
关键字结合使用。
Future详解
Future
是 Dart 异步编程的基础,它表示一个将在未来完成的计算。Future
有两种状态:未完成(pending) 和 完成(completed) 。当一个异步操作完成时,Future
要么返回结果(成功),要么抛出异常(失败)。
创建 Future
Future
可以通过多种方式创建,包括 Future.value()
、Future.error()
和异步函数 async
的返回值。
示例:使用 Future.value()
dart
void main() {
Future<String> future = Future.value('Hello, Dart Future!');
future.then((value) => print(value));
}
示例:使用异步函数
dart
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
完成。在调用该函数时,主线程不会被阻塞。
then
和 catchError
处理结果
使用 Future
时,结果通常通过 then()
方法处理,错误通过 catchError()
处理。then()
方法会在 Future
完成后执行回调。
示例:处理结果和错误
dart
void main() {
Future<int> future = Future(() {
return 42;
});
future.then((value) {
print('Future completed with value: $value');
}).catchError((error) {
print('An error occurred: $error');
});
}
async
和 await
在 Dart 中,async
和 await
提供了一种简洁的语法来处理异步操作,使代码看起来更像同步代码。
示例:使用 async
和 await
dart
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)。
- 单订阅流:只能有一个监听器。
- 广播流:可以有多个监听器,通常用于事件广播。
示例:单订阅流
dart
void main() {
Stream<int> stream = Stream.fromIterable([1, 2, 3, 4, 5]);
stream.listen((value) {
print('Stream value: $value');
});
}
示例:广播流
dart
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
dart
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
dart
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*
dart
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*
dart
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 中的异步编程通过 Future
、Stream
和生成器函数为开发者提供了强大的工具,帮助处理复杂的异步操作。Future
用于处理一次性的异步任务,Stream
适合处理一系列异步事件,而生成器函数则提供了简洁的异步数据生成机制。
通过合理使用异步编程模式,开发者可以编写出高效、响应迅速的 Flutter 应用。在实际开发中,掌握这些异步工具对于提升应用性能和用户体验至关重要。