前言
我们在开发flutter应用的时候编写代码,要么是同步代码,要么是异步代码。那么什么是同步什么是异步呢?
- 同步代码就是正常编写的代码块
- 异步代码就是Future,async等关键字修饰的代码块
一、时机不同
他们区别于运行时机不同,同步代码先执行,异步代码后执行,即使你的同步代码写在最后,那也是你的同步代码执行,之后运行你的异步代码。
二、机制不同
异步代码运行在 event loop中,类似于Android里的Looper机制,是一个死循环,event loop不断的从事件队列里取事件然后运行。
event loop循环机制
如图所示,事件存放于队列中,loop循环执行
image.png
Dart的事件循环如下图所示。循环中有两个队列。一个是微任务队列(MicroTask queue),一个是事件队列(Event queue)。
image.png
事件队列包含外部事件,例如I/O, Timer,绘制事件等等。
微任务队列则包含有Dart内部的微任务,主要是通过scheduleMicrotask来调度。
- 首先处理所有微任务队列里的微任务。
- 处理完所有微任务以后。从事件队列里取1个事件进行处理。
- 回到微任务队列继续循环。
Dart要先把所有的微任务处理完,再处理一个事件,处理完之后再看看微任务队列。如此循环。
例子:
8个微任务
2个事件
Dart-->执行完8个微任务
Dart-->执行完1个事件
Dart-->查看微任务队列
Dart-->再执行完1个事件
done
异步执行
那么在Dart中如何让你的代码异步执行呢?很简单,把要异步执行的代码放在微任务队列或者事件队列里就行了。
可以调用scheduleMicrotask来让代码以微任务的方式异步执行
scheduleMicrotask((){
print('a microtask');
});
可以调用Timer.run来让代码以Event的方式异步执行
Timer.run((){
print('a event');
});
Future异步执行
创建一个立刻在事件队列里运行的Future:
Future(() => print('立刻在Event queue中运行的Future'));
创建一个延时1秒在事件队列里运行的Future:
Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中运行的Future'));
创建一个在微任务队列里运行的Future:
Future.microtask(() => print('在Microtask queue里运行的Future'));
创建一个同步运行的Future:
Future.sync(() => print('同步运行的Future'));
这里要注意一下,这个同步运行指的是构造Future的时候传入的函数是同步运行的,这个Future通过then串进来的回调函数是调度到微任务队列异步执行的。
有了Future之后, 通过调用then来把回调函数串起来,这样就解决了"回调地狱"的问题。
Future(()=> print('task'))
.then((_)=> print('callback1'))
.then((_)=> print('callback2'));
在task打印完毕以后,通过then串起来的回调函数会按照链接的顺序依次执行。
如果task执行出错怎么办?你可以通过catchError来链上一个错误处理函数:
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'));
上面这个Future执行时直接抛出一个异常,这个异常会被catchError捕捉到。类似于Java中的try/catch机制的catch代码块。运行后只会执行catchError里的代码。两个then中的代码都不会被执行。
既然有了类似Java的try/catch,那么Java中的finally也应该有吧。有的,那就是whenComplete:
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'))
.whenComplete(()=> print('whenComplete'));
无论这个Future是正常执行完毕还是抛出异常,whenComplete都一定会被执行。
结果执行
把如上的代码在dart中运行看看输出
print('1');
var fu1 = Future(() => print('立刻在Event queue中运行的Future'));
Future future2 = new Future((){
print("future2 初始化任务");
});
print('2');
Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中运行的Future'));
print('3');
var fu2 = Future.microtask(() => print('在Microtask queue里运行的Future'));
print('4');
Future.sync(() => print('同步运行的Future')).then((value) => print('then同步运行的Future'));
print('5');
fu1.then((value) => print('then 立刻在Event queue中运行的Future'));
print('6');
fu2.then((value) => print('then 在Microtask queue里运行的Future'));
print('7');
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'));
print('8');
Future(()=> throw 'we have a problem')
.then((_)=> print('callback1'))
.then((_)=> print('callback2'))
.catchError((error)=>print('$error'))
.whenComplete(()=> print('whenComplete'));
print('9');
Future future4 = Future.value("立即执行").then((value){
print("future4 执行then");
}).whenComplete((){
print("future4 执行whenComplete");
});
print('10');
future2.then((_) {
print("future2 执行then");
future4.then((_){
print("future4 执行then2");
});
});
输出
I/flutter (29040): 1
I/flutter (29040): 2
I/flutter (29040): 3
I/flutter (29040): 4
I/flutter (29040): 同步运行的Future
I/flutter (29040): 5
I/flutter (29040): 6
I/flutter (29040): 7
I/flutter (29040): 8
I/flutter (29040): 9
I/flutter (29040): 10
I/flutter (29040): 在Microtask queue里运行的Future
I/flutter (29040): then 在Microtask queue里运行的Future
I/flutter (29040): then同步运行的Future
I/flutter (29040): future4 执行then
I/flutter (29040): future4 执行whenComplete
I/flutter (29040): 立刻在Event queue中运行的Future
I/flutter (29040): then 立刻在Event queue中运行的Future
I/flutter (29040): future2 初始化任务
I/flutter (29040): future2 执行then
I/flutter (29040): future4 执行then2
I/flutter (29040): we have a problem
I/flutter (29040): we have a problem
I/flutter (29040): whenComplete
I/flutter (29040): 1秒后在Event queue中运行的Future
输出说明:
- 先输出同步代码,再输出异步代码
- 通过then串联起的任务会在主要任务执行完立即执行
- Future.sync是同步执行,then执行在微任务队列中
- 通过Future.value()函数创建的任务是立即执行的
- 如果是在whenComplete之后注册的then,那么这个then的任务将放在microtask执行
Completer
Completer允许你做某个异步事情的时候,调用c.complete(value)方法来传入最后要返回的值。最后通过c.future的返回值来得到结果,(注意:宣告完成的complete和completeError方法只能调用一次,不然会报错)。
例子:
test() async {
Completer c = new Completer();
for (var i = 0; i < 1000; i++) {
if (i == 900 && c.isCompleted == false) {
c.completeError('error in $i');
}
if (i == 800 && c.isCompleted == false) {
c.complete('complete in $i');
}
}
try {
String res = await c.future;
print(res); //得到complete传入的返回值 'complete in 800'
} catch (e) {
print(e);//捕获completeError返回的错误
}
}
网友评论