Dart 异步编程相关概念简述

​ 学习 Dart 的异步编程时,须要对异步编程所涉及的相关知识体系进行梳理,咱们可根据如下几个发问来逐个了解异步编程涉及的内容:数组

  • 为何须要异步编程?
  • 异步编程的内在机制是什么?
  • Dart 中如何进行异步编程?

isolate:

Dart是单“线程”语言:异步

Dart 代码在某个 isolate 的上下文中运行,该 isolate 拥有 Dart 代码所需的全部内存。当Dart 代码正在执行时,同一个 isolate 中的其余代码都没法运行,更通俗地讲,Dart 一次执行一个操做,这意味着只要一个操做正在执行,它就不会被任何其余 Dart 代码中断。这就引起出如下问题:async

1,若是 Dart 代码正在执行长耗时计算,或者等待 I/O,将会致使程序冻结;ide

2,若是须要同时运行不一样的 Dart 代码,就须要将这些代码放在不一样的 isolate 环境中执行 ;异步编程

3, 如何在 Dart 中编写异步代码,进行异步操做?(所谓异步操做,指的是当你的程序在等待其它操做完成时,可让其先完成其它部分的操做)oop

咱们先放码出来:学习

main() {
  fallInLove();//谈恋爱
  House newHourse = buyHouse();//买房(耗时、费力操做)
  livingHouse(newHourse);//买完房子以后住进新房
  marry();//结婚
  haveChild();//生娃
}

如上所示,在这种状况下,只有当第一条语句执行完以后,后面的语句才能接着按顺序执行;ui

大部分人的人生三件事:买房,结婚,生娃线程

这三件大事,若是用 Dart 语言来实现:

若是是在同步操做中,而且家里没矿,六个钱包也凑不齐首付的话,你的人生就会冻结- -等着买完房以后,才能结婚,结完婚以后才能生娃;

有没有什么操做能够实现先结婚,再生娃,再买房呢?答案是有的:若是将买房(buyHouse())放在异步操做里,这时候你要给你的丈母娘及老婆一个承诺:之后有钱了再买房!如今先结婚、生娃!详情咱们后文再叙,这里先提出这么个设想;

event loop:

当开始运行 Flutter 或者 Dart 应用程序时,一个 isolate 就会被建立并启动,当 isolate 建立成功时,Dart 就会进行以下三个事项:

1,初始化两个先进先出(FIFO)队列:一个称为 MicroTask 队列,另一个称为 Event 队列;

2,执行main()方法,且执行完成后,进入3

3,启动 Event Loop

4,开始处理两个队列中的元素(两个队列的执行有前后顺序,见后文)

也就是说,每一个 isolate 中都会有且只有一个Event Loop(事件循环)和两个队列(MicroTask Queue、Event Queue ); Event Loop 将根据MicroTask队列和Event队列里的内容来驱动代码的执行方式和顺序。

Add the main isolate executing tasks off the queue: main(), then key event handler, then click event handler, then timer task, etc.

那么问题来了:

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,重复上述两个步骤直到两个队列都没有任何内容可执行

综上所述,能够由以下简化图来表示:

flowchart: main() -> microtasks -> next event -> microtasks -> ...

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());

使用 async/await 关键字有如下几个须要注意的点:

  • async 关键字声明的方法,须要返回 Future 对象:有可用值时类型为 Future< T>;无可用值时为 Future< void>;
  • await 关键字只能在 标记为 async 的方法里出现;
  • await 关键字后面跟着的表达式,一般是 Future 对象,若是不是,系统会自动封装成Future 对象;
  • await 表达式会使表达式以后的语句暂停执行(async方法里的执行),直到返回的 Future对象可用;
  • 对应于使用 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 一致的;

总结

本文主要涉及到的概念有:isolate,event loop,future,async/await,理解了这些内容,可让咱们更好地写出、阅读异步编程的相关代码;

参考连接

参考连接1:https://dart.dev/tutorials/language/futures

参考连接2:https://dart.dev/guides/language/language-tour#asynchrony-support

参考连接3:https://dart.dev/articles/archive/event-loop#event-queue-new-future

相关文章
相关标签/搜索