• Dart 代码库中有大量返回 Future 或 Stream 对象的函数,这些函数都是 异步的,它们会在耗时操作(比如I/O)执行完毕前直接返回而不会等待耗时操作执行完毕。
  • async 和 await 关键字用于实现异步编程,并且让代码看起来就像是同步一样。

处理Future

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// 可以通过两种方式,获得 Future 执行完成的结果。
// 1. 使用 async 和 await
// 2. 使用 Future API

// 使用 async 和 await 的代码是异步的, 下列代码使用 await 等待异步函数的执行结果。

void main() {
print("Hello World0");
checkVersion();
print("Hello World1");

var future = checkVersion();
print("Hello World2");

future
.then((value) => print("异步结果"))
.catchError((error) => print(error))
.whenComplete(() => print("完成"));
print("Hello World3");

}

Future<void> checkVersion() async {
var version = await lookUpVersion();
print(version);
}

Future<String> lookUpVersion() async {
var second = Duration(seconds: 5);
sleep(second);
return "1.0.0";
}

// Hello World0
// Hello World1
// Hello World2
// Hello World3
// 1.0.0
// 1.0.0
// 异步结果
// 完成

// 尽管异步函数可以处理耗时操作,但是它不会等待这些耗时操作完成,异步函数执行时会在其遇到第一个 await 表达式(代码行) 时返回一个 Future 对象,然后等待 await 表达式 执行完毕后继续执行

// 可以使用 try、catch 已经 finally 来处理 await 导致的异常。
void funcTry() async {
try {
var version = await lookUpVersion();
print(version);
} catch (e) {}
}

// 在异步函数中可以多次使用 await 关键字。
void funcAsync() async {
var entrypoint = await findEntryPoing();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
}

// await 表达式的返回值通常是一个 Future 对象;如果不是的话也会自动将其包裹在一个 Future 对象里。Future 对象代表一个"承诺",await 表达式会阻塞知道需要的对象返回。
// 如果使用 await 时导致编译错误,请确保 await 在一个异步函数中使用。例: 如果想在main() 函数中使用 await, 那么 main() 函数必须使用 async 关键字标识。
void main() async {
print("Hello World0");
await lookUpVersion();
}

// 如果使用了声明为 async 的函数 checkVersion(), 但没有等待其结果。在世界开发中,如果假设函数已经执行完成,则可能导致一些异步问题。想要避免这些问题。在在后面加上unawaited
void main() {
checkVersion();
}

Future<void> checkVersion() async {
var version = await lookUpVersion();
print(version);
}

// 好的
Future doSomething() => ...;

void main() async {
await doSomething();
unawaited(doSomething()); // Explicitly-ignored fire-and-forget.
}

// 坏的
void main() async {
doSomething();
}


处理Stream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 可以通过两种方式,从 Stream 中获取值
// 1. 使用 async 关键字和一个异步循环 (使用 await for 关键字标识)。
// 2. 使用 Stream API。

//// 在使用 await for 关键字前,确保其可以令代码逻辑更加清晰并且是真的需要等待所有的结果执行完毕。例如, 通常不应该在 UI 事件监听器上使用 await for 关键字,因为 UI 框架发出的事件流是无穷尽的。

// await for 定义异步循环
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
// 表达式的类型必须是 Strean, 执行流程如下:
// 1. 等待直到 Stream 返回一个数据
// 2. 使用 1 中 Stream 返回的数据执行循环体。
// 3. 重复1、2 过程直到 Stream 数据返回完毕
// 使用 break 和 return 语句可以停止接收 Stream 数据,这样就跳出了循环并取消注册监听 Stream。

//. 使用 Stream 的 listen() 方法来订阅文件列表,传入一个搜索文件或目录的函数
void streamListenMain(List<String> arguments) {
FileSystemEntity.isDirectory("search").then((isDir) {
if (isDir) {
final startingDir = Directory("search");
startingDir.list().listen((entity) {
if (entity is File) {
//
}
});
} else {
//
}
});
}

// 使用 await 表达式和异步 for 循环 (await for) 实现的等价的代码
void streamAwaitForMain(List<String> arguments) async {
if (await FileSystemEntity.isDirectory("path")) {
final startingDir = Directory("path");
await for (final entity in startingDir.list()) {
if (entity is File) {
//
}
}
} else {
//
}
}