@TryLoveCatch
2020-05-08T19:25:07.000000Z
字数 7625
阅读 1003
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");
});
结果:
f2
f1.then()
f2.then()
f2 then
f1
f1 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()
f1
f1 then
f2
f2 then
f3
f3 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()
f1
f2
f2 then
f1.then()
f2 then end
f1 then
Future.microtask
Future.microtask then
f3
f3 then
new 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);
});
结果:
f1
f1 then
Exception: 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参数
结果:
onError
Exception: futrue
我们把throw Exception("futrue");
给注释了,那么结果如下:
f1
f1 then
catchError
Exception: 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);
结果:
catchError1
Exception: futrue then
我们把throw Exception("futrue");
给注释了,那么结果如下:
f1
f1 then
catchError2
IntegerDivisionByZeroException
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");
});
结果:
catchError
Exception: futrue
whenComplete
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 E
foo E
bar E
main X
foo 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 {
}
}