dart异步

在 Dart 2 中,异步编程是一个核心特性,它使得处理耗时操作(如网络请求、文件 I/O、数据库操作等)变得更加高效和简洁。Dart 提供了几种机制来支持异步编程,包括 Future、async/await 和 Stream。

1. Future

Future 是 Dart 中表示异步操作结果的对象。一个 Future 对象代表一个可能还未完成的操作,它最终会产生一个结果值或一个错误。

  • 创建和使用 Future
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Future<String> fetchUserOrder() {
// 模拟一个耗时操作
return Future.delayed(Duration(seconds: 2), () => 'Large Latte');
}

void main() {
print('Fetching user order...');
fetchUserOrder().then((order) {
print('Your order is: $order');
}).catchError((error) {
print('Error: $error');
});
print('Fetching order in progress...');
}

在这个例子中,fetchUserOrder 函数返回一个 Future,它模拟了一个耗时 2 秒的操作。通过 then 方法可以处理 Future 完成后的结果,通过 catchError 方法可以捕获可能发生的错误。

2. async/await

async 和 await 是 Dart 中用于简化异步代码编写的语法糖。使用 async 关键字标记的函数会返回一个 Future,而 await 关键字可以暂停函数的执行,直到 Future 完成。

  • 使用 async/await
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Future<String> fetchUserOrder() async {
// 模拟一个耗时操作
await Future.delayed(Duration(seconds: 2));
return 'Large Latte';
}

void main() async {
print('Fetching user order...');
try {
String order = await fetchUserOrder();
print('Your order is: $order');
} catch (error) {
print('Error: $error');
}
print('Fetching order in progress...');
}

在这个例子中,fetchUserOrder 函数被标记为 async,并使用 await 关键字等待 Future 完成。main 函数也被标记为 async,并使用 await 关键字等待 fetchUserOrder 的结果。这种方式使得异步代码看起来更像同步代码,更易于理解和维护。

3. Stream

Stream 是 Dart 中用于表示异步数据序列的对象。一个 Stream 可以产生多个值(事件),这些值可以是数据或错误,最终可以完成或永远不完成。

  • 创建和使用 Stream
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}

void main() async {
print('Counting to 5...');
countStream(5).listen((data) {
print(data);
}, onError: (error) {
print('Error: $error');
}, onDone: () {
print('Done');
});
print('Counting in progress...');
}

在这个例子中,countStream 函数返回一个 Stream,它每隔一秒产生一个数字。通过 listen 方法可以监听 Stream 的事件,处理数据、错误和完成事件。

4. 错误处理

在异步编程中,错误处理非常重要。Future 和 Stream 都提供了错误处理机制。

  • Future 的错误处理
1
2
3
4
5
6
7
8
9
10
11
12
Future<String> fetchUserOrder() {
return Future.delayed(Duration(seconds: 2), () => throw Exception('Failed to load order'));
}

void main() {
print('Fetching user order...');
fetchUserOrder().then((order) {
print('Your order is: $order');
}).catchError((error) {
print('Error: $error');
});
}

yield 关键字

在 Dart 中,yield 关键字用于生成一个值并将其发送给 Stream 的订阅者。使用 yield 可以创建一个异步生成器函数,这样的函数会返回一个 Stream,可以逐步产生多个值,而不是一次性返回一个结果。

  1. 异步生成器函数
    异步生成器函数使用 async* 关键字来定义,并且可以使用 yield 关键字来逐步生成值。这些值会被发送到 Stream 的订阅者。

  2. yield 的作用
    当使用 yield 时,函数会暂停执行并发送一个值给订阅者。下一次当订阅者请求下一个值时,函数会从上次暂停的地方继续执行。

  3. async* 和 yield 的示例
    以下是一个具体示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
await Future.delayed(Duration(seconds: 1)); // 模拟异步操作
yield i; // 生成一个值并发送给订阅者
}
}

void main() async {
print('Counting to 5...');
await for (int count in countStream(5)) {
print(count);
}
print('Counting done.');
}

  • 定义 countStream 异步生成器函数:
    使用 async* 关键字定义这个函数。
    使用 yield 关键字逐步生成值。

  • 生成值并发送给订阅者:
    在循环中,每隔一秒生成一个值并使用 yield 将其发送给订阅者。
    在主函数中订阅 Stream 并处理值:

  • 使用 await for 循环来订阅 Stream 并处理生成的每个值。

  1. yield* 关键字
    除了 yield,Dart 还提供了 yield* 关键字,它用于将另一个生成器的输出直接传递给当前生成器的订阅者。以下是一个简单示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Stream<int> numbers() async* {
yield 1;
yield 2;
yield 3;
}

Stream<int> moreNumbers() async* {
yield 0;
yield* numbers(); // 将 `numbers` 生成的所有值传递给当前生成器
yield 4;
}

void main() async {
await for (int number in moreNumbers()) {
print(number);
}
}

代码解释:

  • 定义 numbers 异步生成器函数:
    使用 yield 生成几个整数值。

  • 定义 moreNumbers 异步生成器函数:
    使用 yield* 将 numbers 生成的所有值传递给当前生成器。
    生成额外的值。

  • 在主函数中订阅 Stream 并处理值:
    使用 await for 循环来订阅 Stream 并处理生成的每个值。