异步支持
Dart库中有许多返回Future或Stream对象的类型。这些函数是异步的:它们在设置可能耗时的操作(例如I/O)后返回,而不等待该操作完成。
async和await关键字用来支持异步编程。
处理Futures
当我们需要的结果在将来返回时,有两种实现方式:
- 使用async和await关键字。
- 使用Future API。
使用async和await的异步代码,看起来很像同步代码。例如:
1
await lookUpVersion();
/Users/txooo/Documents/Blogs/_posts/2019-07-15-dart_05.md 如果使用了await,那么必须在函数名后使用async来标记函数为异步函数:
1
2
3
4
5
6
7
Future checkVersion() async {
await lookUpVersion();
}
Future lookUpVersion() async {
// ...
}
- 虽然异步函数可能会执行耗时的操作,但它不会等待这些操作。相反,异步函数只在遇到第一个await表达式时执行。然后它返回一个Future对象,仅在await表达式完成后才恢复执行。
使用try,catch和finally时使用await以:
1
2
3
4
5
6
7
8
9
try {
// assert(1==2);
var version = await checkVersion();
print('version is :$version');
} catch (e) {
print(e);
} finally {
print('over');
}
在一个函数中,await可以多次使用。
1
2
3
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在await表达式中,表达式的值通常发生在将来。如果不是,那么该值将自动包装到Future中。这个Future表示对返回一个对象的承诺。await表达式的值是返回的对象,它会使执行暂停,知道对象可用时恢复。
一定要确保await在一个异步函数中,否则使用await在编译时会抛出错误。即使是在main函数中使用await,也必须要使用async来声明main()是异步的。
1
2
3
4
Future main() async {
checkVersion();
print('In main: version is ${await lookUpVersion()}');
}
声明一步函数
一个异步函数是用使用了async关键字标记的普通函数产生的。在一个函数中添加async官架子使其返回一个Future,它还可以用于匿名函数,例如:
1
2
// 匿名函数,返回一个字符串
String syncLookUpVersion() => '1.0.0';
修改为异步函数:
1
Future<String> asyncLpokUpVersion() async => '1.0.0';
- 函数的主体不需要使用Future API,如有必要,Dart会创建Future对象,如果函数没有返回值,可以将其返回类型设置为Future
。
处理Stream
当需要从Stream获取值时,也有两种方法:
- 使用async和异步for循环(asynchronous for loop)(即await for)
- 使用Stream API。
使用await for之前,要确保它能使代码更清晰,并且您确实希望等待所有Stream的结果。例如,通常不应该是哟好难过await for去处理UI事件监听,因为UI框架会发送无休止的事件流。
await for形式:
1
2
3
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
表达式的值必须具有Stream类型,执行过程如下:
- 等待stream发送一个值,
- 执行for循环主体,将变量设置为发出的值,
- 重复1和2,直到stream关闭。
要终止监听stream,可以使用break或return语句,然后会在for循环断开处取消stream订阅。
同样,await for也需要确保在异步函数中使用。main()也不例外:
1
2
3
4
5
6
7
Future main() async {
// ...
await for (var request in requestServer) {
handleRequest(request);
}
// ...
}
具体示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Future awaitForExample() async {
var stream = countStream(10);
var sum = await sumStream(stream);
print(sum);
}
// 使用async*和yield生成简单的整数流,下面会介绍
Stream<int> countStream(int number) async* {
for (int i = 0; i < number; i++) {
yield i;
}
}
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (var value in stream) {
sum += value;
}
return sum;
}
生成器
当需要惰性生成一系列值时,可以使用生成器函数。Dart内置了两种生成器:
- 同步生成器(Synchronous generator),返回Iterable(可迭代)对象。
- 异步生成器(Asynchronous generator),返回Stream(流)对象。
使用sync*和yield来实现同步生成器函数:
1
2
3
4
5
6
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) {
yield k++;
}
}
使用async*和yield来实现异步生成器函数:
1
2
3
4
5
6
Stream<int> asyncNaturalsTo(int n) async* {
int k = 0;
while (k < n) {
yield k++;
}
}
如果生成器是递归的,可以使用yield*来提高性能:
1
2
3
4
5
6
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
Isolates
大多数计算机,即使在移动平台上,也有多核CPU。为了充分利用这些核心,开发人一般采用并发运行的共享内存线程。但是,共享状态并发容易出错,并且可能导致代码复杂化。
所有Dart代码都在隔离区内运行,而不是线程。每个隔离区都有自己的内存堆,确保不会从任何其他隔离区访问隔离区的状态。
使用时需要导入:
1
import 'dart:isolate';
Dart是单线程模型,但是使用Isolates可以用于多线程。