关键点
- dart是单线程语言
- 同步代码会阻塞你的程序
- 使用
Future
对象来执行异步操作 - 在async函数里使用
await
关键字来挂起执行知道一个Future操作完成 - 或者使用
then()
方法 - 在async函数里使用try-catch表达式捕获错误
- 或者使用
catchError()
方法 - 可以使用链式操作future对象来按顺序执行异步函数
dart是一个单线程的编程语言,如果编写了任何阻塞执行线程的代码(例如耗时计算或者I/O),程序就会被阻塞。异步操作可以让你在等待一个操作完成的同时完成其他工作。Dart使用Future
对象来进行异步操作。
介绍
先来看一个会导致阻塞的程序代码:
// Synchronous code
void printDailyNewsDigest() {
var newsDigest = gatherNewsReports(); // Can take a while.
print(newsDigest);
}
main() {
printDailyNewsDigest();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
}
复制代码
程序会收集当天的新闻并且打印出来(耗时操作),然后打印一些用户感兴趣的其他信息。
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny.
Baseball score: Red Sox 10, Yankees 0
复制代码
这段代码是有问题的,当printDailyNewsDigest()
方法做耗时操作阻塞,剩下的其他代码无论多长时间都只有等到printDailyNewsDigest()
返回结果之后才能继续执行。
为了帮助保持应用程序的响应,Dart库的作者在定义可能做耗时工作的函数时用了异步模型。这些函数的返回值是一个future。
future是什么
一个future是一个Future的泛型Future
对象,代表了一个异步操作产生的T类型的结果。如果结果的值不可用,future的类型会是Future
,当返回一个future的函数被调用了,将会发生如下两件事: 1.这个函数加入待完成的队列并且返回一个未完成的Future
对象。 2.接着,当这个操作结束了,Future
对象返回一个值或者错误。
编写返回一个future的代码时,有两面两个可选方法:
- 使用async和await关键字
- 使用
Future
API
async 和 await
async
和 await
关键字是Dart语言异步支持的一部分。允许你不使用Future api像编写同步代码一样编写异步代码。一个异步函数的函数体前面要生命async
关键字,await
关键字仅在异步函数里生效。
下面的代码就是一个使用了Future api的例子。
import 'dart:async';
Future<void> printDailyNewsDigest() {
final future = gatherNewsReports();
return future.then(print);
// You don't *have* to return the future here.
// But if you don't, callers can't await it.
}
main() {
printDailyNewsDigest();
printWinningLotteryNumbers();
printWeatherForecast();
printBaseballScore();
}
printWinningLotteryNumbers() {
print('Winning lotto numbers: [23, 63, 87, 26, 2]');
}
printWeatherForecast() {
print("Tomorrow's forecast: 70F, sunny.");
}
printBaseballScore() {
print('Baseball score: Red Sox 10, Yankees 0');
}
const news = '' ;
const oneSecond = Duration(seconds: 1);
// Imagine that this function is more complex and slow. :)
Future<String> gatherNewsReports() =>
Future.delayed(oneSecond, () => news);
复制代码
在main()
方法里面printDailyNewsDigest()
是一个被调用的,但是因为等待了一秒延迟执行,所以在最后才被打印出来。这个程序的执行顺序如下:
1.程序开始执行
2.main方法调用printDailyNewsDigest()
,但是不会立即返回,会调用gatherNewsReports()
3.gatherNewsReports()
开始收集新闻同时返回一个Future
。
4.printDailyNewsDigest()
使用then()
指定一个Future的响应结果,调用then()
返回一个新完成的Future结果作为他的回调。
5.剩下的print方法是同步的会依次执行。
6.当所有的新闻都被收集到了,带有新闻信息的Future被gatherNewsReports()
返回。
7.then()
方法执行,将future返回的String作为参数传递给print
打印。
future.then(print)
等同于future.then((newsDigest) => print(newsDigest))
所以printDailyNewsDigest()
还可以写成:
Future<void> printDailyNewsDigest() {
final future = gatherNewsReports();
return future.then((newsDigest) {
print(newsDigest);
// Do something else...
});
}
复制代码
newsDigest是gatherNewsReports()
返回的结果。
即时这个Future的类型是Future
,也需要传递一个参数给then()
的回调,直接用下划线_
表示。
Future<void> printDailyNewsDigest() {
final future = gatherNewsReports();
return future.then((_) {
// Code that doesn't use the `_` parameter...
print('All reports printed.');
});
}
复制代码
处理错误
在Future api里,可以使用catchError()
捕获错误:
Future<void> printDailyNewsDigest() =>
gatherNewsReports().then(print).catchError(handleError);
复制代码
如果请求不到新闻并且失败了,上面的代码将会执行:
1.gatherNewsReports()
完成返回的future携带了error
2.then()
方法中的print不会被执行
3.catchError()
捕获到处理错误,future被catchError()
正常完成并返回,错误不会继续传递下去。
更多细节阅读 Futures and Error Handling
调用多个函数返回futures
有三个函数,expensiveA()
、 expensiveB()
和 expensiveC()
, 它们都返回Future对象。你可以顺序调用它们(one by one),或者也可以同时调用三个函数并且当所有结果都返回时再做处理。Future api支持以上的操作。
用then()进行链式调用
按顺序使用then()调用每个方法
expensiveA()
.then((aValue) => expensiveB())
.then((bValue) => expensiveC())
.then((cValue) => doSomethingWith(cValue));
复制代码
这是个嵌套调用,上一个函数的返回结果作为下一个函数的参数。
使用Future.wait()等待多个方法一起完成
如果好几个函数的执行顺序无关紧要,可以使用Future.wait()
。
Future.wait([expensiveA(), expensiveB(), expensiveC()])
.then((List responses) => chooseBestResponse(responses, moreInfo))
.catchError(handleError);
复制代码
当传递一个future列表给Future.wait()
时,它会立即返回一个Future,但是直到列表里的future都完成的时候,这个Future才会完成,他会返回一个列表里面是每一个future产生的结果数据。
如果任何一个调用的函数产生了错误,都会被catchError()
捕获到去处理错误。
相关资料
- Futures and Error Handling
- The Event Loop and Dart
- Asynchrony support
- Future API reference