每种语言都会有,并且需要有异步操作,同样地,Dart使用过程中,也是需要有异步操作的。
我们在Java , Oc中可以使用线程来实现异步操作,但是Dart是单线程的,想要实现异步操作的话,我们可以使用事件队列来处理。
isolate
英文直译为“使孤立的,使隔离的“。
Dart代码在某个isolate的上下文中运行,该isolate拥有Dart 代码所需的所有内存。就如同一条赛道,时不时就会有车需要加油,换轮胎等。如果直接停在这一条路上,不但影响别的赛车的行驶,也会造成安全隐患。这就迫切需要一个服务站,在服务站内调整之后,重回赛道。
event loop
基于上面我们提到的场景,我们引入了event loop ,事件处理的一个队列。当Dart程序被创建,会有一个isolate被创建,并且会有以下三个事项被执行:
1,初始化两个FIFO队列,一个为MicroTask队列,一个为Event队列(从名字上我们可以简单推断出,MicroTask的任务耗时比较少,侧面推测出,可能优先级也会较高)。
2.执行main()方法
3.启动Event loop
4.处理两个队列中的任务。
那么问题来了:
1,Event Loop是什么?用来干啥的?
2,MicroTask队列和Event队列都分别是什么?有什么用?
3,两者有什么区别?
Event Loop是一个定期唤醒的无限循环:它在MicroTask、Event队列中查找是否有需要运行的任务。如果队列中的任务存在,则当且仅当CPU空闲时,Event Loop将它们放入运行堆栈执行。
MicroTask Queue用于非常短的,需要异步运行的操作,考虑如下场景:想要在稍后完成一些任务但又希望是在执行下一个Event队列之前;一般使用dart:async库中的scheduleMicrotask方法来实现;
Event Queue(事件队列)包含所有外部事件:
I/O,
手势,
鼠标事件,
绘图事件,
计时器,
isolate 之间的消息
Future
每次外部事件被触发时,要执行的相应代码都会被添加到 Event Queue 中,当MicroTask队列中没有任何内容时,Event Loop才会从Event 队列中取出第一项来处理;需要重点关注的是,Future也会被添加到 Event 队列中;
当main()方法执行完成后,event loop开始它的工作,
1,先从 microtask 队列以先进先出的方式取出并执行完所有内容;
2,从event 队列中取出并处理第一项;
3,重复上述两个步骤直到两个队列都没有任何内容可执行
综上所述,可以由如下简化图来表示:
我们可以清楚地看到,程序永远是优先处理MicoroTask中的任务,然后再处理Event Queue中的数据。
Future
Future 通常指的是异步运行的任务,它会在未来某个时间点完成,这里的完成有两层含义:成功或者失败,成功时返回任务执行的结果(注意:这里的结果指的是 Future< T> 返回范型T的对象),失败时返回错误;
当实例化一个 Future 的时候:
该 Future 的一个实例被创建并记录在由 Dart 管理的内部数组中;
需要由此 Future 执行的代码直接被推送到 Event 队列中;
Future 实例返回一个未完成状态的 Future 对象;
如果 Future 语句之后还有其它的话(不是 Future 包含着的代码),则继续执行下一个同步代码;
Future 完成后,then()方法及catchError()方法里的代码将会被执行;
import 'dart:async';
void main() {
fallInLove(); //谈恋爱;
handleHouse(); //买房、入住(耗时、费用的操作)
marry(); //结婚
haveChild(); //生娃
}
///进行买房 [buyHouse]、入住[livingHouse]等操作
void handleHouse() {
Future house = buyHouse();
house.then((_) {
livingHouse();
});
}
class House {}
Future buyHouse() {
Future result = Future(() {
print('buyHouse');
});
return result;
}
void livingHouse() {
print('livingHouse');
}
void marry() {
print('marry');
}
void haveChild() {
print('haveChild');
}
void fallInLove() {
print('fall in love');
}
我们来分析上面的方法的执行顺序:
1.从main()方法中开始执行同步代码,首先执行fallInLove();
2.执行handleHouse()方法,将Future里的(){print('buyHouse');}加入 Event 队列;
3.执行marry()方法;
4.执行haveChild()方法;
5.执行到这里的时候,main()方法已经执行完了,Event Loop开始处理两个队列中的元素,如前面的分析,这时候先查看MicroTask队列有没有需要处理的任务,没有的话,就可以取出Event队列中的第一个任务来执行,在这个例子中,就是开始执行步骤2的(){print('buyHouse');}代码块;
6.当上述步骤完成之后,开始执行then()中的方法 livingHouse();
执行上面的代码的打印结果应该是:
fall in love
marry
haveChild
buyHouse
livingHouse
async/await
上面的 Future 章节,我们主要使用了 Future 的 API 来达到异步操作的目的,Dart 还为我们提供了一对关键字 async/await 来达成此目的;有了这两个关键字,我们可以像写同步代码那样写异步代码,并且不用使用到 Future的 API (then());
1.async 关键字声明的方法,需要返回 Future 对象:有可用值时类型为 Future< T>;无可用值时为 Future< void>;
2.await 关键字只能在 标记为 async 的方法里出现;
3.await 关键字后面跟着的表达式,通常是 Future 对象,如果不是,系统会自动封装成Future 对象;
4.await 表达式(async方法里的执行),直到返回的 Future对象可用;
5.对应于使用 Future API 中的 catchError()方法,可以将await表达式包在 try/catch进行错误捕获及后续处理;
如下:我们只需要稍微改造handleHouse()方法:
1.在方法的 body 前加 async关键字标志
2.将 handleHouse()方法的返回类型改为 Future< void>
3.在需要耗时的操作方法前加 await关键字标志
///进行买房 [buyHouse]、入住[livingHouse]等操作
Future handleHouse() async {
await buyHouse();
livingHouse();
}
运行代码后的输出效果是与使用Future API 一致的;
几个
1.await关键字必须在async函数内部使用
2.调用async函数必须使用await关键字
3.await使用之后,会阻塞后面的代码执行,只到返回的 Future对象可用。这一点儿理解需要注意。