Asynchrony support
Dart 库中充满了返回 Future 或 Stream 对象的函数。 这些函数是异步的:它们在设置一个可能耗时的操作(例如 I/O)后返回,而不等待该操作完成。
async 和 await 关键字支持异步编程,让编写看起来类似于同步代码的异步代码。
参考:Future class - dart:async library - Dart API
Stream class - dart:async library - Dart API
Handling Futures
需要已完成的 Future 的结果时,有两种选择:
- 使用异步和等待。
- 使用 Future API。
使用 async 和 await 的代码是异步的,但它看起来很像同步代码。 例如,下面是一些使用 await 等待异步函数结果的代码:
await lookUpVersion();
要使用 await,代码必须在异步函数中——标记为异步的函数:
Future<void> checkVersion() async {var version = await lookUpVersion();// Do something with version
}
注意:虽然异步函数可能会执行耗时的操作,但它不会等待这些操作。 相反,async 函数只会执行到它遇到第一个 await 表达式。 然后它返回一个 Future 对象,仅在 await 表达式完成后才恢复执行。
在使用 await 的代码中使用 try、catch 和 finally 来处理错误和清理:
try {version = await lookUpVersion();
} catch (e) {// React to inability to look up the version
}
可以在一个异步函数中多次使用 await。 例如,以下代码等待函数结果三次:
var entrypoint = await findEntryPoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
在 await 表达式中,表达式的值通常是一个 Future; 如果不是,则该值会自动包装在 Future 中。 此 Future 对象指示返回对象的承诺。 await 表达式的值是返回的对象。 await 表达式使执行暂停,直到该对象可用。
void main() async {checkVersion();print('In main: version is ${await lookUpVersion()}');
}
注意:前面的示例使用异步函数 (checkVersion()) 而不等待结果——如果代码假定函数已完成执行,这种做法可能会导致问题。
Declaring async functions
异步函数是函数体标有 async 修饰符的函数。
将 async 关键字添加到函数使其返回 Future。 例如,考虑这个同步函数,它返回一个字符串:
String lookUpVersion() => '1.0.0';
把它改成一个异步函数——例如,因为Future的实现会很耗时——返回的值是一个Future:
Future<String> lookUpVersion() async => '1.0.0';
请注意,函数的主体不需要使用 Future API。 如果需要,Dart 会创建 Future 对象。 如果函数没有返回有用的值,请将其返回类型设置为 Future<void>。
Handling Streams
需要从Stream中获取值时,有两种选择:
- 使用异步和异步 for 循环(等待)。
- 使用 Stream API。
注意:在使用 await for 之前,请确保它使代码更清晰,并且确实想要等待Stream的所有结果。 例如,通常不应该为 UI 事件侦听器使用 await for,因为 UI 框架会发送无穷无尽的事件流。
异步 for 循环具有以下形式:
await for (varOrType identifier in expression) {// Executes each time the stream emits a value.
}
expression 的值必须具有 Stream 类型。 执行过程如下:
- 等到Stream发出一个值。
- 执行 for 循环的主体,并将变量设置为发出的值。
- 重复 1 和 2,直到Stream关闭。
要停止收听Stream,可以使用 break 或 return 语句,它会跳出 for 循环并取消订阅流。
如果在实现异步 for 循环时出现编译时错误,请确保 await for 位于异步函数中。 例如,要在应用程序的 main() 函数中使用异步 for 循环,必须将 main() 的主体标记为异步:
void main() async {// ...await for (final request in requestServer) {handleRequest(request);}// ...
}
异步编程参考:
Library tour | Dart
Generators
需要延迟生成一系列值时,请考虑使用生成器函数。 Dart 内置支持两种生成器函数:
- 同步生成器:返回一个 Iterable 对象。Iterable class - dart:core library - Dart API
- 异步生成器:返回一个 Stream 对象。https://api.dart.dev/stable/dart-async/Stream-class.html
实现一个同步生成器函数,将函数体标记为sync*,使用yield语句传递值:
Iterable<int> naturalsTo(int n) sync* {int k = 0;while (k < n) yield k++;
}
要实现异步生成器函数,将函数体标记为 async*,并使用 yield 语句传递值:
Stream<int> asynchronousNaturalsTo(int n) async* {int k = 0;while (k < n) yield k++;
}
如果你的生成器是递归的,你可以通过使用 yield* 来提高它的性能:
Iterable<int> naturalsDownFrom(int n) sync* {if (n > 0) {yield n;yield* naturalsDownFrom(n - 1);}
}
Callable classes
要允许 Dart 类的实例像函数一样被调用,请实现 call() 方法。
call() 方法允许任何定义它的类来模拟一个函数。 此方法支持与普通函数相同的功能,例如参数和返回类型。
class WannabeFunction {String call(String a, String b, String c) => '$a $b $c!';
}var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');void main() => print(out);Log
Hi there, gang!
Isolates
大多数计算机,甚至在移动平台上,都具有多核 CPU。 为了利用所有这些内核,开发人员传统上使用并发运行的共享内存线程。 然而,共享状态并发很容易出错,并可能导致代码复杂化。
所有 Dart 代码都运行在 isolates 中,而不是线程。 每个 Dart isolate 使用单个执行线程并且不与其他 isolate 共享可变对象。 旋转多个隔离创建多个执行线程。 这使得多线程没有其主要缺点,竞争条件。https://en.wikipedia.org/wiki/Race_condition#In_software
- Concurrency in Dart
- dart:isolate API reference, including Isolate.spawn() and TransferableTypedData
- Background parsing cookbook on the Flutter site
- Isolate sample app
Typedefs
类型别名——通常称为 typedef,因为它是用关键字 typedef 声明的——是一种引用类型的简洁方式。 下面是声明和使用名为 IntList 的类型别名的示例:
typedef IntList = List<int>;
IntList il = [1, 2, 3];
类型别名可以有类型参数:
typedef ListMapper<X> = Map<X, List<X>>;
Map<String, List<String>> m1 = {}; // Verbose.
ListMapper<String> m2 = {}; // Same thing but shorter and clearer.
在大多数情况下,建议对函数使用内联函数类型而不是 typedef。 但是,函数 typedef 仍然有用:
typedef Compare<T> = int Function(T a, T b);int sort(int a, int b) => a - b;void main() {assert(sort is Compare<int>); // True!
}