Dart中的异步操作

每种语言都会有,并且需要有异步操作,同样地,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,重复上述两个步骤直到两个队列都没有任何内容可执行

综上所述,可以由如下简化图来表示:

image.png

我们可以清楚地看到,程序永远是优先处理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> house = buyHouse();
  house.then((_) {
    livingHouse();
  });
}

class House {}

Future<House> buyHouse() {
  Future<House> 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 表达式$color{red}{会使表达式之后的语句暂停执行}$(async方法里的执行),直到返回的 Future对象可用;
5.对应于使用 Future API 中的 catchError()方法,可以将await表达式包在 try/catch进行错误捕获及后续处理;

如下:我们只需要稍微改造handleHouse()方法:

1.在方法的 body 前加 async关键字标志
2.将 handleHouse()方法的返回类型改为 Future< void>
3.在需要耗时的操作方法前加 await关键字标志

///进行买房 [buyHouse]、入住[livingHouse]等操作
Future<void> handleHouse() async {
  await buyHouse();
  livingHouse();
}

运行代码后的输出效果是与使用Future API 一致的;

几个$color{red}{注意点!!}$

1.await关键字必须在async函数内部使用
2.调用async函数必须使用await关键字
3.await使用之后,会阻塞后面的代码执行,只到返回的 Future对象可用。这一点儿理解需要注意。

暂无评论

相关推荐

Dart中String使用格式化

在java和OC中,String对象都可以格式化样式,而在Dart 中,我们还需要导入一个三方的库进行这个操作。引用库 dependenc …

Dart中使用单例

class GYDBBaseManager{ static GYDBBaseManager _instance ; static GYDBBaseManager get instance => _getInstanc …

Dart中的隐式接口

先看一下文档的描述:每个类都隐式地定义一个接口,该接口包含类的所有实例成员及其实现的任何接口。如果您想创建一个 …

微信扫一扫,分享到朋友圈

Dart中的异步操作