作用
async和await是用来简化异步代码的API,使用async和await可以轻松的写出类似同步代码的异步代码,函数会更加清晰易读。
使用简介
在学习使用async和await之前,你需要先掌握Dart中Future类的使用,因为async和await本质上只是对Future类的简化。
学习async和await之前,我们先来看一段使用Future类的代码:
void main() {
int num;
getNum().then((value) {
num = value;
print(num);
});
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
这段代码很简单,将异步函数getNum()返回的值赋值给num,打印num。
现在我们使用async和await来简化上面的代码:
void main() async{
int num;
num = await getNum();
print(num);
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
首先我们将main方法后加入async,使用async的目的就是告诉Dart我要在这里使用await了。
正常情况下getNum()函数返回的是一个Future对象,当我们在getNum()前加入await时,await getNum()获取到的是Future完成后返回的值,也就是上面代码中的int类型的1。
如果将代码改成下面这样:
void main() async{
int num;
num = getNum();
print(num);
}
代码会报错,因为getNum返回的类型是Future<int>。
await关键字的另一个功能是阻塞线程,当程序遇到await时,代码会等待await修饰的方法执行完成之后,再执行await后的代码,就像同步代码那样。
继续拿上面的代码举例:
void main() async{
int num;
num = await getNum();
print(num);
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
当程序执行到 num = await getNum()时会等待2s,等到getNum执行结束。
这就是如何在Dart中使用async和await的全部内容了。
异常处理
我们都知道Future类有一个catchError方法,能够帮助我们处理异常
示例如下:
Future(() {
throw StateError('This is a Dart exception in Future.');
}).catchError((dynamic e, StackTrace stack) => print(e), test: (e) => true);
那么我们在使用async和await时该如何处理异常呢?
答案很简单,我们只需要像同步代码那样使用try,catch处理异常就可以了。
示例如下:
void main() async {
try {
int num;
num = await getNum();
print(num);
} catch (e) {}
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
使用场景
场景一
学习了这么多async和await的用法,我们什么使用Future,什么时候使用async呢?
Dart官方建议使用async,因为代码可读性很高,而且可以避免Future的嵌套地狱,当你的Future链式调用很长,又加上一堆catchError,读起来让人很痛苦。
但是有一些情况我们必须使用Future来完成我们的工作。
当你想在一个函数里执行异步操作,但又不想该函数返回Future,你就需要使用Future.then(),就像下面的代码示例一样。
main() {
int num;
getNum().then((value) {
num = value;
print(num);
});
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
使用future不会影响main方法的返回值类型
当我们使用async时:
main() async{
int num;
num = await getNum();
print(num);
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
返回值为Future<dynamic>类型,是一个Future对象。
场景二
如果你在一个函数里有多个异步操作,但他们之间没有关联,不想让第二个函数等待第一个函数完成,使用Future是更好的选择,因为await会阻塞代码。
我们接着使用上面的代码示例来给你演示区别
这是使用Future的代码示例:
void main() {
int num;
getNum().then((value) {
num = value;
print(num);
});
print("同步代码");
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
输出如下:
同步代码 1
这是使用async的代码示例:
void main() async{
int num;
num = await getNum();
print(num);
print("同步代码");
}
Future<int> getNum() {
return Future.delayed(Duration(seconds: 2), () => 1);
}
输出如下:
1 同步代码
async程序等待getNum运行结束,才运行下面的代码,所以先输出1,再输出同步代码,
而使用Future链式调用的代码因为是异步的,所以先输出同步代码,等到getNum结束再输出1。
这里涉及到Dart的Event Loop(事件循环)及dart如何用单线程处理异步,你可以先记住结论await会阻塞代码,像同步程序那样。
await for
就像使用await处理Future一样,await for是用来处理Stream的。
使用for循环来处理Stream返回的数据,在Stream完成前,代码会一直被阻塞。
再学习await for之前,你应该掌握Dart中的Stream。
代码示例:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
//sum不断累加 直到Stream完成
await for (var value in stream) {
sum += value;
}
return sum;
}
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
yield i;
}
}
main() async {
//获取到Stream
var stream = countStream(10);
//调用方法
var sum = await sumStream(stream);
print(sum); // 55
}
countStream主要用来创建流,sumStream方法展示了如何使用await for。
值得一提的是,使用await for处理Stream就像使用await和Future一样,都是等待异步操作完成,代码才继续向下执行。