dart异步代码是如何执行的
在Dart基础语法一文中我们了解了dart的一些基础语法,包括异步代码的使用。在dart中,异步代码占有很重要的地位,到处都可以发现异步代码。许多的库函数都会返回Future对象。那么,当我们编写了异步代码之后,这些代码究竟是什么时候执行的呢?先来看下面的代码
import 'dart:async';
void main() {
Future future1 = Future(() => print('future1'));
Future future2 = Future(() => print('future2'));
Future future3 = Future(() => print('future3'));
Future future4 = Future(() => print('future4'));
print('main 1');
Future future5 = Future.value("future5").then((onValue) {
print(onValue);
print('future5.then');
}).whenComplete(() {
print('future5 complete');
});
Future future6 = Future.value(future1).then((_) {
print('future6.then');
}).whenComplete(() {
print('future6 complete');
});
future4.then((_) {
print('future4.then');
});
Future future7 = Future.sync(() {
print('future7');
}).then((_) {
print('future7.then');
}).whenComplete(() {
print('future7 complete');
});
future3.then((_) {
print('future3.then1');
future2.then((_) {
print('future2.then');
});
scheduleMicrotask((){print('scheduleMicrotask on future3.then1');});
}).then((_) => print('future3.then2'));
print('main 2');
future1.then((_) {
print('future1.then');
});
future5.then((_) {
print('future5.then2');
});
}
那么上面的输出是什么呢?
乍一看可能比较晕。也许现在你还不知道如何去分析。但是当你了解Dart任务调度之后,便可以自行分析出上面的程序是如何得到这样的输出结果的。
Event Loop(事件循环)、Event Queue(事件队列)和Microtask Queue微任务队列
dart是单线程模型,(支持Isolates:一种在另一个线程上运行Dart代码的方法
),关于Isolate本文不进行过多讨论,后续会另外写一篇文章分享)。默认情况下dart代码在主Isolate执行(相当于Android的UI线程),由Event Loop
驱动。并且包含了一个Event Queue
和一个Microtask Queue
dart代码执行的过程如下图所示
可以看到,当main()执行完成后,Event Loop
开始工作。首先,它以FIFO(先进先出)顺序执行微任务队列中的所有的微任务,直到微任务队列为空,然后将事件队列中的第一项出队并处理,并且重复该循环:执行所有微任务,然后处理事件队列中的下一项
注意:当事件循环从微任务队列执行任务时,事件队列被卡住,应用程序将无法绘制图形,响应IO等操作
将任务添加到事件队列和微任务队列
-
添加到事件队列
-
new Future(FutureOr<T> computation())
添加一个任务到事件队列的队尾 -
Future.delayed(Duration duration, [FutureOr<T> computation()])
将任务延时添加到事件队列的队尾
-
-
添加到微任务队列
- 使用顶级函数
scheduleMicrotask(void callback())
将任务添加到微任务队列的队尾 - 使用
Future.microtask(FutureOr<T> computation())
将任务添加到微任务队列的队尾
- 使用顶级函数
-
以上是将任务入队的常用手段,也是推荐使用的。通过上面的方法可以更好的提高我们编写的程序的可读性。并且能够让程序的执行更好的符合我们的预期结果,而不是出现一些意料之外的情况。除此之外还需要注意下面列举的情况:
-
Future.value([FutureOr<T> value])
的构造的Future在微任务队列里调度完成,如果传入的value是Future,那么该任务会等待到传入的Future结束后执行 -
Future.sync(FutureOr<T> computation())
会同步执行(立即执行)其构造参数的函数(除非该构造参数的函数返回Future),并且构造的Future在微任务队列调度完成。注意new Future()
和Future.delayed()
的构造参数的函数不是立即执行,而是添加任务到事件队列 - 首先Future的
then()
并没有将任务添加到任何队列中,它仅仅只是注入了一个回调,在Future任务执行完成之后会执行该回调方法,并且会将Future的结果传递过去。在如果在调用一个Future的then()
之前该Future已经执行完成了,那么该任务会被添加到微任务队列
-
分析
在了解了上面的内容之后,便可尝试分析前面的程序,有助于更好的理解前面所说的内容
-
为了便于讲解,画个简图如下所示(执行完
main()
后事件队列和微任务队列中的情况)
-
按照任务调度图中的流程,直接执行的放在main区域。
执行
Future future1 = Future(() => print('future1'));
,将任务() => print('future1')
添加到事件队列执行
Future future2 = Future(() => print('future2'));
, 将任务() => print('future2')
添加到事件队列执行
Future future3 = Future(() => print('future3'));
, 将任务() => print('future3')
添加到事件队列执行
Future future4 = Future(() => print('future4'));
, 将任务() => print('future4')
添加到事件队列执行
print('main 1');
,放入mian区域
执行
Future future5 = Future.value("future5").then((onValue) { print(onValue); print('future5.then'); }).whenComplete(() { print('future5 complete'); });
,
a. 执行Future future5 = Future.value("future5")
,首先传入的不是Future,不需要等待,并且该Future会在微任务队列完成,所以添加到微任务队列,future5
任务实际上就是设置了value
b. 执行then((onValue) {print(onValue); print('future5.then'); })
,注入then回调监听((onValue) {print(onValue); print('future5.then'); }
)到future5
上,等待future5
任务执行后回调,
c. 执行whenComplete(() {print('future5 complete');});
,注入一个回调监听(() {print('future5 complete');}
)到future5 回调监听后,与then()
有点相似,区别是即使Future执行出错了,还是会调用whenComplete
回调,而不会调用then()
,并且then()
可以接收值,``whenComplete()不接收。同样是等待
future5整个执行完成后回调,有点类似于
"finally" block`执行
Future future6 = Future.value(future1).then((_) {print('future6.then'); }).whenComplete(() { print('future6 complete');});
a. 执行Future future6 = Future.value(future1)
,首先传入的future1是Future,所以,将future6假装是一个listener,插入到future1后,如果future1已经有listener,则找到最后一个并插到最后
b.执行then((_) {print('future6.then'); })
,注入listener到future6后,实际就是future1后
c.执行whenComplete(() {print('future6 complete');});
,同样注入回调监听(() {print('future6 complete');}
)执行
future4.then((_) {print('future4.then');});
,注入回调监听((_) {print('future4.then');}
)到future4
后。执行
Future future7 = Future.sync(() { print('future7');}).then((_) { print('future7.then'); }).whenComplete(() {print('future7 complete'); });
a. 执行Future future7 = Future.sync(() { print('future7');})
,首先构造参数函数(() { print('future7');}
)会立即执行,放入main区域
,并且future7 会在微任务队列调度完成,添加到微任务队列
b.执行then((_) { print('future7.then'); })
,注册回调监听((_) { print('future7.then'); }
)到future7后
c. 执行whenComplete(() {print('future7 complete'); });
,注册回调监听(() {print('future7 complete'); }
)执行
future3.then((_) {print('future3.then1'); future2.then((_) { print('future2.then');}); scheduleMicrotask((){print('scheduleMicrotask on future3.then1');}); }).then((_) => print('future3.then2'));
a. 执行then((_) {print('future3.then1'); future2.then((_) { print('future2.then');}); scheduleMicrotask((){print('scheduleMicrotask on future3.then1');}); })
,注册回调监听((_) {print('future3.then1'); future2.then((_) { print('future2.then');}); scheduleMicrotask((){print('scheduleMicrotask on future3.then1');}); }
)到future3后
b. 执行then((_) => print('future3.then2'));
,注册回调监听((_) => print('future3.then2')
)到future3的回调最后执行
print('main 2');
,放入main区域
执行
future1.then((_) {print('future1.then'); });
,注册回调监听((_) {print('future1.then'); }
)到future1的回调最后执行
future5.then((_) { print('future5.then2');});
,注册回调监听((_) { print('future5.then2');}
)到future5
当前的listener最后
-
接下来按照调度执行,看看会怎么样输出
- 最先执行main:输出
"main1"
->"future7"
->"main2"
,main执行完毕 - 检查微任务队列不为空,按照FIFO次序依次执行
a. 先执行第一个微任务future5
,为future5
设置了个value("future5"),
b. 执行future5
的then
回调(onValue) { print(onValue); print('future5.then'); }
,并且将value("future5")传递过去,输出"future5"
->"future5.then"
c. 执行future5
的whenComplete
回调() {print('future5 complete');
,输出"future5 complete"
d. 执行future5
的第二个then
回调(_) { print('future5.then2');}
,输出"future5.then2"
e. 执行第二个微任务future7
,
f. 执行future7
的then
回调(_) { print('future7.then'); }
,注意此时onValue为null,一般不关注的参数可以用_
代替,输出"future7.then"
- 微任务队列执行完,为空,检查事件队列不为空,接下来执行事件队列
a. 第一个任务,future1
:() => print('future1')
出队,输出"future1"
,future1
执行完毕,依次调用回调监听(_) {print('future6.then'); }
、() {print('future6 complete');
、(_) {print('future1.then'); }
,输出"future6.then"
->"future6 complete"
->"future1.then"
,执行完毕检查微任务队列为空,执行事件队列任务
b. 第二个任务future2
:() => print('future2')
出队,输出"future2"
,检查微任务队列为空,
c. 第三个任务future3
:() => print('future3')
出队,输出"future3"
,future3
执行完毕,依次调用回调监听
d.执行future3
第一个then
回调监听(_) {print('future3.then1'); future2.then((_) { print('future2.then');}); scheduleMicrotask((){print('scheduleMicrotask on future3.then1');}); }
;首先执行print('future3.then1');
,输出"future3.then1"
,然后执行future2.then((_) { print('future2.then');})
,注意future2
在前面3_a步骤中已经被执行完了,所以任务(_) { print('future2.then');}
会被添加到微任务队列,接下来执行scheduleMicrotask((){print('scheduleMicrotask on future3.then1');});
,添加任务(){print('scheduleMicrotask on future3.then1');}
到微任务队列,注意此时只是提交任务到微任务队列,还未执行任务
e.执行future3
第二个then
回调监听(_) => print('future3.then2')
,输出"future3.then2"
,执行完毕, - 检查微任务队列不为空,执行微任务队列
a. 执行微任务(_) { print('future2.then');}
,输出"future2.then"
,检查微任务队列不为空
b. 执行微任务(){print('scheduleMicrotask on future3.then1');}
,输出"scheduleMicrotask on future3.then1"
- 微任务队列执行完,为空,检查事件队列不为空,继续执行事件队列
a.执行任务future4
:() => print('future4')
,输出"future4"
,future4
执行完毕,调用回调监听(_) {print('future4.then');}
,输出"future4.then"
- 最先执行main:输出
至此,我们通过推理验证了文章开头的程序输出。
Future.then链式调用的补充
注意到前面的例子每个Future.then
中的任务实际上都是return null
的,那么如果是return Future
对象,对任务的调度会有什么影响呢?
- 看下面的程序,试着分析输出是什么
import 'dart:async';
main() {
print('main1');
new Future(() => print('future1'))
.then((_) { Future(() => print('future2(a new future) in future1.then1'));})
.then((_) => print('future1.then2'));
new Future(() => print('future3'));
scheduleMicrotask(() => print('microtask'));
print('main2');
}
- 结果如下
- 和上面的分析过程一样,先执行
main
,输出main1
->main2
,执行完毕,检查微任务队列不为空 - 执行微任务
() => print('microtask')
输出microtask
,微任务队列为空 - 执行事件队列,先执行任务
future1
,输出future1
,执行完成后调用回调监听(_) { Future(() => print('future2(a new future) in future1.then1'));}
,创建了一个Future,将任务() => print('future2(a new future) in future1.then1')
提交到事件队列的最后,再执行回调(_) => print('future1.then2')
,输出future1.then2
,检查微任务队列为空 - 执行事件队列任务
future3
:() => print('future3')
,输出future3
,检查微任务队列为空 - 执行事件队列任务
() => print('future2(a new future) in future1.then1')
,输出future2(a new future) in future1.then1
- 接下来看下面的程序,注意与前面的区别
import 'dart:async';
main() {
print('main1');
new Future(() => print('future1'))
.then((_) { return Future(() => print('future2(a new future) in future1.then1'));}) //注意此处的区别, 将future2 return回去
// .then((_)=> Future(() => print('future2(a new future) in future1.then1'))) //也可写成这种写法
.then((_) => print('future1.then2'));
new Future(() => print('future3'));
scheduleMicrotask(() => print('microtask'));
print('main2');
}
- 输入结果如下
- 可以看到前面的输出
main1
->main2
->microtask
->future1
还是没有问题,后面的由future1.then2
->future3
->future2(a new future) in future1.then1
变成了future3
->future2(a new future) in future1.then1
->future1.then2
- 实际上由于
future1.then1
将future2
return
回去,所以future1.then2
会在future2
执行之后进行回调,而future2
被插入到事件队列的最后面,所以任务future3
先执行,先输出future3
,然后执行future2
,输出future2(a new future) in future1.then1
,最后执行future1.then2
,输出future1.then2
验证
在参考文章The Event Loop and Dart中有下面这么一段程序,如果可以正确的推出输出结果的话,相信能够更好的理解dart任务调度的整个流程
import 'dart:async';
main() {
print('main #1 of 2');
scheduleMicrotask(() => print('microtask #1 of 3'));
new Future.delayed(new Duration(seconds:1),
() => print('future #1 (delayed)'));
new Future(() => print('future #2 of 4'))
.then((_) => print('future #2a'))
.then((_) {
print('future #2b');
scheduleMicrotask(() => print('microtask #0 (from future #2b)'));
})
.then((_) => print('future #2c'));
scheduleMicrotask(() => print('microtask #2 of 3'));
new Future(() => print('future #3 of 4'))
.then((_) => new Future(
() => print('future #3a (a new future)')))
.then((_) => print('future #3b'));
new Future(() => print('future #4 of 4'));
scheduleMicrotask(() => print('microtask #3 of 3'));
print('main #2 of 2');
}
- 输出结果如下,你推理对了吗??