@TryLoveCatch
2020-05-08T11:25:07.000000Z
字数 7625
阅读 1236
flutter
https://www.jianshu.com/p/54da18ed1a9e
在Dart中线程叫做isolate。应用程序启动后,开始执行main函数并运行main isolate。
每个isolate包含一个事件循环以及两个事件队列,event loop事件循环,以及event queue和microtask queue事件队列。
但和普通Thread不同的是,isolate拥有独立的内存,isolate由线程和独立内存构成。正是由于isolate线程之间的内存不共享,所以isolate线程之间并不存在资源抢夺的问题,所以也不需要锁。
�又由于内存是各自独立的,相互之间并不能进行访问。但isolate提供了基于port的消息机制,通过建立通信双方的sendPort和receiveport,进行相互的消息传递,在Dart中叫做消息传递。
isolate看起来其实和进程比较相似,isolate的整体模型其实更像进程,而async、await更像是线程。
- Dart应用程序仅有一个消息循环,以及两个队列:event事件队列和microtask微任务队列。
- 在Main Isolate执行main函数之后,当main函数执行完毕退出后,Main Isolate的线程开始一个个的处理队列中的内容。所以,当main函数exit后,消息循环开始工作。
- 事件队列负责处理I/O事件、绘制事件、手势事件、接收其他isolate消息等外部事件
- 微任务队列则包含有Dart内部的微任务,主要是通过scheduleMicrotask来调度。
如下图所示:

Dart的事件循环的运行遵循以下规则:
注意第一步里的所有,也就是说在处理事件队列之前,Dart要先把所有的微任务处理完。如果某一时刻微任务队列里有8个微任务,事件队列有2个事件,Dart也会先把这8个微任务全部处理完再从事件队列中取出1个事件处理,之后又会回到微任务队列去看有没有未执行的微任务。
总而言之,就是对微任务队列是一次性全部处理,对于事件队列是一次只处理一个。
根据上面的规则,我们可以预测任务执行的顺序,但是我们无法预测事件循环什么时候会从队列中提取任务。
例如,当你创建一个延迟任务的时候,事件会在你指定的时间插入到队列末尾,但是并不会立即执行,需要排队等待之前的事件都执行完毕,微任务队列同样如此。
需要注意的是,当执行微任务事件时,会阻塞事件t队列的事件执行,这样就会导致渲染、手势响应等事件事件响应延时。为了保证渲染和手势响应,应该尽量将耗时操作放在事件队列中。
调用scheduleMicrotask来让代码以微任务的方式异步执行
scheduleMicrotask((){print('a microtask');});
调用Timer.run来让代码以Event的方式异步执行
Timer.run((){print('a event');});
仅仅使用回调函数来做异步的话很容易陷入“回调地狱(Callback hell)”,为了避免这样的问题,JS引入了Promise。同样的, Dart引入了Future。
Future就是延时操作的一个封装,可以将异步任务封装为Future对象。
要使用Future的话需要引入dart.async
import 'dart:async';
Future(() => print('立刻在Event queue中运行的Future'));
Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中运行的Future'));
Future.microtask(() => print('在Microtask queue里运行的Future'));
Future.sync(() => print('同步运行的Future'));
这里要注意一下:
- Future.sync()的同步运行指的是回调函数是同步运行的,会立马执行
- Future.sync()通过then串进来的回调函数是调度到微任务队列异步执行的。
我们看一个例子吧:
Future f1 = new Future((){print("f1");});Future f2 = Future.sync(() {print("f2");});print("f1.then()");f1.then((_) {print("f1 then");});print("f2.then()");f2.then((_) {print("f2 then");});
结果:
f2f1.then()f2.then()f2 thenf1f1 then
结果:
Future.sync()的回调函数同步执行,并且then之后的会调度到微任务队列中去
Future(()=> throw 'we have a problem').then((_)=> print('callback1')).then((_)=> print('callback2')).catchError((error)=>print('$error')).whenComplete(()=> print('whenComplete'));
这里也需要注意一下:
- then串起来的回调函数会按照链接的顺序依次执行
- then串起来的回调函数有可能同步也有可能异步
- catchError可以捕获task和then的异常
- whenComplete一定会被执行,无论Future是正常执行完毕还是抛出异常
针对上买呢的第二条,我们看下面的注意事项
1、当Future里面的函数执行完成后,then()注册的回调函数会立即执行。需注意的是,then()注册的函数并不会添加到事件队列中,回调函数只是在事件循环中任务完成后被调用。也就是说它们是同步执行,而不是被调度异步执行。
2、如果Future在调用then()(仅仅是调用到then()方法,并没有执行then里面的函数)之前已经完成,那么这些回调函数会被调度到微任务队列异步执行
3、Future()和Future.delayed()构造函数并不会立即完成计算。
4、Future.value()构造函数在微任务中完成计算,其他类似第2条。
5、Future.sync()构造函数会立即执行函数,并在微任务中完成计算,其他类似第2条。
为了方便理解1和2,我们先看一个 简单例子:
Future f1 = new Future((){print("f1");});Future f2 = new Future(() {print("f2");});Future f3 = new Future(() {print("f3");});print("f3.then()");f3.then((_) {print("f3 then");});print("f2.then()");f2.then((_) {print("f2 then");});print("f1.then()");f1.then((_) {print("f1 then");});
结果:
f3.then()f2.then()f1.then()f1f1 thenf2f2 thenf3f3 then
解析:
0、添加f1、f2、f3依次到事件队列
1、f3.then()、f2.then()、f1.then()先执行了,表明then()函数先执行,当然里面的回调函数是没有执行的
2、f1执行,因为不符合注意事项2,符合注意事项1,所以f1.then回调函数直接执行
3、f2执行,因为不符合注意事项2,符合注意事项1,所以f2.then回调函数直接执行
4、f3执行,因为不符合注意事项2,符合注意事项1,所以f3.then回调函数直接执行
这里你可能有一个疑问:f3.then()在前面,为啥f1先执行的呢?
因为,最开始
Future f1 = new Future((){print("f1");});Future f2 = new Future(() {print("f2");});Future f3 = new Future(() {print("f3");});
这里会按顺序,把f1,f2,f3依次添加到事件队列里面,所以肯定是f1先执行的。
我们再看一个复杂的例子:
Future f1 = new Future((){print("f1");});Future f2 = new Future(() {print("f2");});Future f3 = new Future(() {print("f3");});print("f3.then()");f3.then((_) {print("f3 then");});print("f2.then()");f2.then((_) {print("f2 then");new Future(() => print("new Future befor f1 then"));print("f1.then()");f1.then((_) {print("f1 then");});new Future.microtask((){print("Future.microtask");}).then((_){print("Future.microtask then");});print("f2 then end");});
结果:
f3.then()f2.then()f1f2f2 thenf1.then()f2 then endf1 thenFuture.microtaskFuture.microtask thenf3f3 thennew Future befor f1 then
解析:
0、f3.then()和f2.then()执行
1、事件队列里面是f1,f2,f3
2、遍历微任务队列,为空,遍历事件队列,f1执行
3、遍历微任务队列,为空,遍历事件队列,f2执行,然后f2.then,因为f2.then()之前已经执行了,所以立即执行f2.then
4、执行f2.then
4.1、new Future()添加到事件队列尾部
4.2、f1.then()执行,因为f1先执行了,所以f1.then添加到微任务队列尾部
4.3、new Future.microtask()添加到微任务队列尾部
4.4、打印f2 then end
5、遍历微任务队列,执行f1.then
6、因为微任务队列里面还有任务,所以执行new Future.microtask()的任务,然后直接执行new Future.microtask().then
7、微任务队列为空,遍历事件队列,f3执行,f3.then因为是在之前就执行了,所以直接执行,不用添加到微任务队列
8、遍历微任务队列,为空,遍历事件队列,new Future()执行
一般来说,我们使用try-catch代码块捕获异常:
try {throw new Exception("Oh, error!");} catch (e) {logger.log(e);}
但是,Future会将任务添加到事件队列,因此计算过程中产生的异常并不在当前代码块中,以致try-catch并不能捕获Future中的异常。
try {/*** 抛出异常,程序崩溃* try-catch不能捕获Future异常*/new Future.error("Oh, error!");} catch (e) {logger.log(e);}
在Futrue里面,我们通过catchError来捕获异常。
new Future((){throw Exception("futrue");print("f1");}).then((_) {print("f1 then");throw Exception("futrue then");}).catchError((error) {print(error);});
结果:
Exception: futrue
结论:
- Future()回调函数里面的异常可以被catchError捕获
- then并不会执行
new Future((){print("f1");}).then((_) {print("f1 then");throw Exception("futrue then");}).catchError((error) {print(error);});
结果:
f1f1 thenException: futrue then
结论:
- then回调函数里面的异常可以被catchError捕获
- catchError可以捕获Future()和then()回调函数里面的异常
- Future()回调函数发生异常后,then不会被执行
如果我们想要区分是Future()的异常还是then()的异常,应该怎么处理呢?
有两种方法:
new Future((){// 这一行throw Exception("futrue");print("f1");}).then((_) {print("f1 then");throw Exception("futrue then");}, onError: (error) {print("onError");print(error);}).catchError((error) {print("catchError");print(error);});
then()增加了onError参数
结果:
onErrorException: futrue
我们把throw Exception("futrue");给注释了,那么结果如下:
f1f1 thencatchErrorException: futrue then
结论:
- Future()回调函数发生异常,被then的onError捕获了
- 而then回调函数发生的异常,被catchError捕获了
这个结论,跟参考的那篇文章正好相反。。。
new Future((){// 这一行throw FormatException("futrue");print("f1");}).then((_) {print("f1 then");throw IntegerDivisionByZeroException();}).catchError((error) {print("catchError1");print(error);}, test: (e) => e is FormatException).catchError((error) {print("catchError2");print(error);}, test: (e) => e is IntegerDivisionByZeroException);
结果:
catchError1Exception: futrue then
我们把throw Exception("futrue");给注释了,那么结果如下:
f1f1 thencatchError2IntegerDivisionByZeroException
catchError()相当于try-catch的镜像函数的话,那么whenComplete()则等同于finally。
无论是正常返回值还是出现异常,都会执行注册的回调函数
我们以上面的例子为例:
new Future((){throw Exception("futrue");print("f1");}).then((_) {print("f1 then");throw Exception();}).catchError((error) {print("catchError");print(error);}).whenComplete((){print("whenComplete");});
结果:
catchErrorException: futruewhenComplete
async和await是什么?它们是Dart语言的关键字,可以让你用同步代码的形式写出异步代码。
看一个例子:
foo() async {print('foo E');String value = await bar();print('foo X $value');}bar() async {print("bar E");return "hello";}main() {print('main E');foo();print("main X");}
执行结果:
main Efoo Ebar Emain Xfoo X hello
我们主要来看这个:
foo() async {print('foo E');String value = await bar();print('foo X $value');}
这个会被分成两部分:
foo() async {print('foo E');Future future = bar()---------------------------String value = await future;print('foo X $value');}
分割线上面的会先执行,await后面的必须是一个Future,然后,分割线下面的就会以then的方式链入这个Future被异步调度执行。
相当于:
foo() {print('foo E');return Future.sync(bar).then((value) => print('foo X $value'));}
- async标注的函数其返回值类型是Future
- await后面必须是Future对象
- await并不像字面意义上程序运行到这里就停下来啥也不干等待Future完成。而是立刻结束当前函数的执行并返回一个Future。函数内剩余代码通过调度异步执行。
- await只能在async函数中出现。
- async函数中可以出现多个await,每遇见一个就返回一个Future, 实际结果类似于用then串起来的回调。
- async函数也可以没有await, 在函数体同步执行完毕以后返回一个Future,相当于第一条
另外,使用async和await还有一个好处是我们可以用和同步代码相同的try/catch机制来做异常处理。
foo() async {try {print('foo E');var value = await bar();print('foo X $value');} catch (e) {// 同步执行代码中的异常和异步执行代码的异常都会被捕获} finally {}}