Future
Future 代表了一个“在未来某个时间点”才会完成的操作结果。 Future 的生命周期可分为两个阶段:
- 未完成 (Uncompleted):异步操作正在进行中,结果尚未产生;
- 已完成 (Completed):操作结束,会有两种可能的结果:
- 成功 (Value):操作顺利完成,返回具体的数据(如 Future
返回字符串); - 失败 (Error):操作中途出错,返回一个异常。
- 成功 (Value):操作顺利完成,返回具体的数据(如 Future
stateDiagram
[*] --> Uncompleted : Create Future
Uncompleted --> Success : Value
Uncompleted --> Failure : Error
Success --> [*] : .then / await
Failure --> [*] : .catchError / try-catch
示例
先通过一个例子来看同步函数与异步函数及其使用上的区别:
// ex721.dart
import 'dart:async';
import 'dart:io';
void main() {
final watch = Stopwatch()..start(); // 3
var count = 0;
void myprint(o) =>
print('out${count++} ${watch.elapsed.inMilliseconds}ms: $o'); // 3a
myprint('before compute');
var result = compute(3, 4); // 4
myprint('after compute');
myprint('result=$result');
myprint('before computeAsync');
var futureResult = computeAsync(5, 12); // 5
myprint('after computeAsync');
futureResult.then((onValue) => myprint('result=$onValue')); // 6
/* An example output:
out0 0ms: before compute
out1 1004ms: after compute
out2 1005ms: result=7
out3 1005ms: before computeAsync
out4 1010ms: after computeAsync
out5 2020ms: result=17
*/
}
// 1
int compute(int a, int b) {
// Simulate a long task
sleep(Duration(seconds: 1));
return a + b;
}
// 2
Future<int> computeAsync(int a, int b) {
return Future(() => compute(a, b));
}
compute是一个同步函数,它模拟了一个耗时约1秒种的计算任务(根据a和b进行计算),然后返回其结果;computeAsync是compute的异步版本,它是一个异步函数,它使用Future.new创建了一个Future对象并返回;Stopwatch是一个用于计时对秒表,精确到微秒(microseconds),该示例使用了毫秒(milliseconds)作为计时单位(3a);- 调用
compute函数,程序阻塞直到compute返回结果(out0 - out2); - 调用
computeAsync函数得到的是一个Future对象,computeAsync函数返回时,真正的计算(compute)还在进行中,即程序不会挂起等待计算结果(out3 - out4); - 给
futureResult.then提供一个callback(回调)函数,当computeAsync内部计算完成时,callback将被调用(out5)。
同步 vs 异步:
| 特性 | 同步函数 (compute) | 异步函数 (computeAsync) |
|---|---|---|
| 定义方式 | int compute(...) | Future<int> computeAsync(...) |
| 返回值 | 直接返回结果 | 返回一个“凭证” Future,承诺以后给结果 |
| 执行表现 | 调用时程序会“停”在这一行直到算完 | 调用后程序可以立即去做别的事 |
| 调用方式 | var res = compute(3, 4); | computeAsync(3, 4).then(...); |
async/wait
上述示例中的computeAsync可以用asyc/wait改写,这是更现代、更具可读性的风格,让异步代码看起来像同步代码。
// ex722.dart
void main() async { // 3
var result = await compute(8, 15); // 2
print(result); // Output: 23
}
// 1
Future<int> compute(int a, int b) async => a + b;
async写在函数(或方法)签名的最后,这里标记compute为异步函数;- 调用异步函数
compute时,使用前缀关键字await,使得异步代码像同步代码那样简洁; await关键字只能在异步函数(或方法)中。
异常处理
下面的例子演示了有关Future的异常处理。
// ex723.dart
void main() async {
divide(2, 1).then((val) => print(val)); // Output: 2
divide(2, 0).then((val) => print(val)).catchError((e) => print(e));
// Output: IntegerDivisionByZeroException
print(await divide(3, 1)); // Output: 3
try {
print(await divide(3, 0));
} on Exception catch (e) {
print(e); // Output: IntegerDivisionByZeroException
}
}
Future<int> divide(int a, int b) async => a ~/ b;
构造函数
常用的 Future 构造函数:
Futrue(computation)(Futrue.new) 使用Timer.run创建一个包含computation()的计算结果的Future(异步执行)Futrue.sync(computation)创建一个包含computation()的计算结果的Future(同步执行);Future.delayed(duration,[computation])延迟一段时间后执行代码(常用于模拟网络延迟)Future.value([value])立即创建一个成功状态的 FutureFuture.error(error, [stackTrace])立即创建一个失败状态的 FutureFuture.microtask(computation)将任务放入微任务队列(Microtask Queue),相比Futrue.new优先级更高
请仔细阅读并分析如下示例。
// ex724.dart
void main() {
final watch = Stopwatch()..start();
var count = 0;
void myprint(o) =>
print('out${count++} ${watch.elapsed.inMilliseconds}ms: $o');
myprint('start');
Future.delayed(Duration(seconds: 1), () => myprint('deplay'));
Future.microtask(() => 'microtask1').then(myprint);
Future(() => 'new').then(myprint);
Future.sync(() => 'sync').then(myprint);
Future.value('value').then(myprint);
Future.error('error').catchError(myprint);
Future.microtask(() => 'microtask2').then(myprint);
/* An example output:
out0 0ms: start
out1 24ms: microtask1
out2 24ms: sync
out3 25ms: value
out4 27ms: error
out5 28ms: microtask2
out6 30ms: new
out7 1014ms: deplay
*/
}
实用方法
Future提供了下列实用的静态方法:
Future.wait(futures,{...})同时启动多个Future,并等待它们全部完成;Future.any(futures)谁先完成就返回谁(无论是成功还是失败);Future.doWhile(action)重复执行异步操作,直到返回false为止。Future.forEach(elements, action)对集合中的每个元素顺序执行异步操作,它会等前一个执行完再开始下一个。
下面的示例代码不难理解,请读者自行分析。
// ex725.dart
import 'dart:math';
void main() {
final watch = Stopwatch()..start();
var count = 0;
void myprint(o) =>
print('out${count++} ${watch.elapsed.inMilliseconds}ms: $o');
Future.wait([
Future(() => myprint('wait1')),
Future(() => myprint('wait2')),
Future(() => myprint('wait3')),
]);
Future.wait([
Future(() => 1),
Future(() => 2),
Future(() => 3),
]).then(myprint);
Future.any([
Future.delayed(Duration(milliseconds: 100), () => myprint('any1')),
Future.delayed(Duration(milliseconds: 100), () => myprint('any2')),
Future.delayed(Duration(milliseconds: 100), () => myprint('any3')),
]);
Future.any([
Future.delayed(Duration(milliseconds: 100), () => 4),
Future.delayed(Duration(milliseconds: 100), () => 5),
Future.delayed(Duration(milliseconds: 100), () => 6),
]).then(myprint);
final rand = Random();
Future.doWhile(() {
var d = rand.nextDouble();
myprint(d);
return d < 0.8;
});
Future.forEach(['A', 'B', 'C'], myprint);
/* An example output:
out0 13ms: 0.06789414914959846
out1 15ms: 0.142393000844255
out2 15ms: 0.7807443343674055
out3 15ms: 0.2542896840253688
out4 15ms: 0.5590822258793036
out5 15ms: 0.8071049146003575
out6 17ms: A
out7 17ms: B
out8 17ms: C
out9 19ms: wait1
out10 21ms: wait2
out11 21ms: wait3
out12 24ms: [1, 2, 3]
out13 110ms: any1
out14 113ms: any2
out15 113ms: any3
out16 114ms: 4
*/
}
Timeout(超时)
Future.timeout()实例方法是一个实用的守时工具。它允许我们为一个异步操作设置一个最大容忍时间(timeLimit)。简单来说,它的逻辑是:“要么在规定时间内给我结果,要么我就抛出异常走人。”
// ex726.dart
void main() async {
var data = await fetchData().timeout(Duration(milliseconds: 2000)); // 1
print(data); // Output: ok
data = await fetchData().timeout(Duration(milliseconds: 500)); // 2
print(data);
/* Output:
Unhandled exception:
TimeoutException after 0:00:00.500000: Future not completed
*/
}
Future<String> fetchData() async {
// Simulate a long task
await Future.delayed(Duration(seconds: 1));
return 'ok';
}
fetchData()函数在timeLimit(2s)到达之前完成了,timeout()方法返回的 Future 会正常返回原任务的结果,就好像timeout()从未存在过一样。timeLimit(0.5s)耗尽时fetchData()函数还没执行完,于是这一行代码抛出了TimeoutException。
通过给timeout()提供 onTimeout 参数 ,可以避免遭遇TimeoutException。 onTimeout 函数返回一个超时的保底值。
// ex727.dart
void main() async {
var data = await fetchData().timeout(
Duration(milliseconds: 500),
onTimeout: () => 'timeout',
);
print(data); // Output: timeout
}
Future<String> fetchData() async {
// ... (omitted for brevity)
}