素钗一生推 2019-11-16
学习 Dart 的异步编程时,需要对异步编程所涉及的相关知识体系进行梳理,我们可根据以下几个发问来逐个了解异步编程涉及的内容:
[TOC]
Dart是单“线程”语言:
Dart 代码在某个 isolate 的上下文中运行,该 isolate 拥有 Dart 代码所需的所有内存。当Dart 代码正在执行时,同一个 isolate 中的其他代码都无法运行,更通俗地讲,Dart 一次执行一个操作,这意味着只要一个操作正在执行,它就不会被任何其他 Dart 代码中断。这就引发出以下问题:
1,如果 Dart 代码正在执行长耗时计算,或者等待 I/O,将会导致程序冻结;
2,如果需要同时运行不同的 Dart 代码,就需要将这些代码放在不同的 isolate 环境中执行 ;
3, 如何在 Dart 中编写异步代码,进行异步操作?(所谓异步操作,指的是当你的程序在等待其它操作完成时,可以让其先完成其它部分的操作)
我们先放码出来:
main() { fallInLove();//谈恋爱 House newHourse = buyHouse();//买房(耗时、费力操作) livingHouse(newHourse);//买完房子之后住进新房 marry();//结婚 haveChild();//生娃 }
如上所示,在这种情况下,只有当第一条语句执行完之后,后面的语句才能接着按顺序执行;
大部分人的人生三件事:买房,结婚,生娃!
这三件大事,如果用 Dart 语言来实现:
如果是在同步操作中,并且家里没矿,六个钱包也凑不齐首付的话,你的人生就会冻结- -等着买完房之后,才能结婚,结完婚之后才能生娃;
有没有什么操作可以实现先结婚,再生娃,再买房呢?答案是有的:如果将买房(buyHouse())放在异步操作里,这时候你要给你的丈母娘及老婆一个承诺:以后有钱了再买房!现在先结婚、生娃!详情我们后文再叙,这里先提出这么个设想;
当开始运行 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队列里的内容来驱动代码的执行方式和顺序。
那么问题来了:
1,Event Loop是什么?用来干啥的?
2,MicroTask队列和Event队列都分别是什么?有什么用?
3,两者有什么区别?
Event Loop是一个定期唤醒的无限循环:它在MicroTask、Event队列中查找是否有需要运行的任务。如果队列中的任务存在,则当且仅当CPU空闲时,Event Loop将它们放入运行堆栈执行。
MicroTask Queue用于非常短的,需要异步运行的操作,考虑如下场景:想要在稍后完成一些任务但又希望是在执行下一个Event队列之前;一般使用dart:async库中的scheduleMicrotask方法来实现;
Event Queue(事件队列)包含所有外部事件:
每次外部事件被触发时,要执行的相应代码都会被添加到 Event Queue 中,当MicroTask队列中没有任何内容时,Event Loop才会从Event 队列中取出第一项来处理;需要重点关注的是,Future也会被添加到 Event 队列中;
当main()方法执行完成后,event loop开始它的工作,
1,先从 microtask 队列以先进先出的方式取出并执行完所有内容;
2,从event 队列中取出并处理第一项;
3,重复上述两个步骤直到两个队列都没有任何内容可执行
综上所述,可以由如下简化图来表示:
Future 通常指的是异步运行的任务,它会在未来某个时间点完成,这里的完成有两层含义:成功或者失败,成功时返回任务执行的结果(注意:这里的结果指的是 Future< T> 返回范型T的对象),失败时返回错误;
当实例化一个 Future 的时候:
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'); }
我们来分析上述代码的执行顺序:
main()
方法中开始执行同步代码,首先执行fallInLove()
;handleHouse()
方法,将Future里的(){print('buyHouse');}
加入 Event 队列;marry()
方法;haveChild()
方法;main()
方法已经执行完了,Event Loop开始处理两个队列中的元素,如前面的分析,这时候先查看MicroTask队列有没有需要处理的任务,没有的话,就可以取出Event队列中的第一个任务来执行,在这个例子中,就是开始执行步骤2的(){print('buyHouse');}
代码块;then()
中的方法 livingHouse()
;;所以代码的执行结果应该如下所示:
fall in love
marry
haveChild
buyHouse
livingHouse
上面的 Future 章节,我们主要使用了 Future 的 API 来达到异步操作的目的,Dart 还为我们提供了一对关键字 async/await 来达成此目的;有了这两个关键字,我们可以像写同步代码那样写异步代码,并且不用使用到 Future的 API (then());
使用 async/await 关键字有以下几个需要注意的点:
如下:我们只需要稍微改造handleHouse()
方法:
handleHouse()
方法的返回类型改为 Future< void>///进行买房 [buyHouse]、入住[livingHouse]等操作 Future<void> handleHouse() async { await buyHouse(); livingHouse(); }
运行代码后的输出效果是与使用Future API 一致的;
本文主要涉及到的概念有:isolate,event loop,future,async/await,理解了这些内容,可以让我们更好地写出、阅读异步编程的相关代码;
参考链接1:https://dart.dev/tutorials/la...