前言
这段时间在学习一些Dart的语法,学到有关异步编程这一块,也许是受之前学过的Java/PHP/C++的影响,就感觉Dart的异步特别别扭,花了好一段时间才算是搞清楚大概是怎么回事,于是在此做下笔记。
正文
async和await的一些规则:
- await关键字必须在被async声明的方法中使用,await可以对同一表达式or函数使用多次(一般一次就够了,只是语法上没有限制)。
- async修饰的方法返回值是Future类型,在没有await关键字声明调用时,无法自动转换成基本类型or自定义类型。
- async修饰的方法会同步执行完第一个await关键字的代码行,然后暂停这一行后面的代码,之后再拿出来执行,可以当做后面的代码都压入了某个“栈”中。
- 虽说async和await关键字组合在Dart中被称为异步编程,但模式是同步非阻塞,即任务仍然是在同一个线程中完成的,不会创建新线程。
先来看几个简单的async例子,之后再说概念感觉好理解一些:
第一个例子
main() {
create();
}
void create() async {
getData1();
getData2();
print("5");
}
void getData1() async {
print("1");
print("2");
}
void getData2() async {
print("3");
print("4");
}
输出结果:
1
2
3
4
5
- 老实说上面的例子加不加async其实没什么不同,但实际上加了async关键字的方法如果有返回值的话,返回的类型是Future,这点稍后举个例子,这个例子这么写主要还是为了避免下面例子被返回值模糊了。
第二个例子
main() {
create();
}
void create() async {
getData1();
getData2();
print("5");
}
void getData1() async {
await print("1");
print("2");
}
void getData2() async {
print("3");
print("4");
}
输出结果:
1
3
4
5
2
这里发现getData1()的一行加了await,结果输出结果顺序就变成了1,3,4,5,2
,原因如下:
- async修饰的方法会同步执行完第一个await关键字的代码行,然后暂停这一行后面的代码,之后再拿出来执行。
第三个例子
main() {
create();
}
void create() async {
getData1();
getData2();
print("5");
}
void getData1() async {
await print("1");
print("2");
}
void getData2() async {
await print("3");
print("4");
}
输出结果:
1
3
5
2
4
这里在第二个例子的基础上,我们在getData2()中也加了await,于是输出结果就变成了1,3,5,2,4
,原因如下:
- 程序首先执行create()
- 执行getData1(),并遇到第一个await关键字,于是执行print("1"),此时控制台输出1,然后把剩下的print("2")放入待执行的栈中。
- 返回并执行getData2(),并遇到第一个await关键字,于是执行print("3"),此时控制台输出3,然后把剩下的print("4")放入待执行栈中。
- 返回并执行print("5"),此时控制台输出5,之后发现栈中还有东西没执行,于是获取栈顶元素,执行print("2"),控制台输出2,继续获取栈顶元素,执行print("4"),控制台输出4,栈顶为空,整个main方法就结束了。
第四个例子
这个例子主要说明async返回值的问题
main() {
create();
}
void create() async{
String data = getData();
print(data);
}
getData() async{
return "1";
}
以上代码会报错:
Uncaught Error: TypeError: Instance of '_Future<dynamic>': type '_Future<dynamic>' is not a subtype of type 'String'
原因就是开头说的,更正方式也很简单:
main() {
create();
}
void create() async {
String data = await getData();
print(data);
}
getData() async {
return await "1";
}