我们在前面已经大致了解了FlutterFuture的运行机制,那么除了FutureFlutter中是否还有其他任务机制呢?

我们先来看一段代码:

  1. void testFuture() {
  2. print('外部代码1');
  3. Future(() => print('任务A')).then((value) => print('A任务结束'));
  4. Future(() => print('任务B')).then((value) => print('B任务结束'));
  5. print('外部代码2');
  6. }

按照我们在之前文章中介绍的Future的用法及执行流程,我们很容易就能猜到运行结果:
image.png
那么,如果在代码运行过程中,突然有紧急任务需要先执行,那么有没有办法处理呢?这就要使用到微任务来做处理了;

scheduleMicrotask

scheduleMicrotask又称为微任务,我们将代码修改一下,添加一个微任务看一下代码的执行流程发生了什么变化?我们将代码修改如下:

  1. void testFuture() {
  2. print('外部代码1');
  3. Future(() => print('任务A')).then((value) => print('A任务结束'));
  4. Future(() => print('任务B')).then((value) => print('B任务结束'));
  5. scheduleMicrotask(() {
  6. print('微任务A');
  7. });
  8. sleep(const Duration(seconds: 1));
  9. print('外部代码2');
  10. }

在代码的执行流程中添加了scheduleMicrotask微任务,那么代码的执行结果是什么呢?
iShot2021-11-14 17.40.49.gif
根据打印信息,我们发现我们在Future后边添加的scheduleMicrotask微任务竟然优先执行了;scheduleMicrotask拥有较高的优先级,进而我们也能够确定在这段代码执行的过程中是有两个队列存在的,否则微任务不发优先执行;

Dart中的队列

Dart中是有两种对类存在的:

  • event queue事件队列:这种队列包含所有的外来事件,如I/Omouse eventsdrawing eventstimersIsolate等之间的信息传递;
  • microtask queue微任务队列:这种队列表示一个短时间内就会完成的异步任务。它的优先级最高,只要此队列中还有任务,就可以一直霸占着事件循环。microtask queue添加的任务主要是由Dart内部产生的。

    需要注意的是,正因为microtask queue队列的优先级高于event queue队列,所以如果在microtask queue中的微任务过多,那么就有可能一直霸占当前的事件循环(event loop)。从而对event queue中的触摸、绘制等事件产生影响,导致这些时间产生阻塞卡顿;

在每一次的事件循环中,Dart总是会优先在microtask queue队列中查询是否还有可执行的任务,如果该队列中没有需要处理的任务,那么才会处理event queue队列中的任务及其流程;

在异步任务中,我们使用的最多的还是优先级较低的event queueDart中为event queue的任务做了一层封装,也就是我们之前使用过的Future

DartFuture异步任务的执行流程:

  • 声明一个Future时,Dart会将异步任务的函数执行体放入event queue中,然后立即返回,后续的代码继续进行同步执行;
  • 当同步执行的代码执行完毕后,event queue会按照加入的顺序也就是声明顺序,依次取出事件,最后同步执行Future的函数体及后续操作;

    事件示例

    我们来定义一串任务,代码如下:

    1. void testFuture() {
    2. Future f1 = Future(() => null);
    3. f1.then((value) {
    4. print('6');
    5. }).then((value) => print('7'));
    6. Future f2 = Future(() => print('1'));
    7. f2.then((value){
    8. print('4');
    9. });
    10. Future(() => print('2'));
    11. scheduleMicrotask(() {
    12. print('3');
    13. });
    14. print('5');
    15. }

    按照我们对Future任务执行顺序的理解,以及scheduleMicrotask具有较高优先级的执行权,那么其打印顺序应该是:

    5、3、6、7、1、4、2

我们查看打印结果:
image.png
确实和我们预料的一样,那么我们将代码作如下修改呢?

  1. void testFuture() {
  2. Future f1 = Future(() => null);
  3. f1.then((value) {
  4. print('6');
  5. scheduleMicrotask(() {
  6. print('8');
  7. });
  8. }).then((value) => print('7'));
  9. Future f2 = Future(() => print('1'));
  10. f2.then((value){
  11. print('4');
  12. });
  13. Future(() => print('2'));
  14. scheduleMicrotask(() {
  15. print('3');
  16. });
  17. print('5');
  18. }

我们在f1的任务中,添加了一个微任务,那么我们来看一下此时的打印结果:
image.png
看到这个结果,我们不免会产生疑问?scheduleMicrotask是微任务,那么他的优先级应该是最高的,那么8为什么会在7的后边打印呢?

此处需要注意的是,then内部的代码,我们可以看做这部分代码被扔在了微任务队列中,而scheduleMicrotask虽然也是微任务,但是此时它仅仅是扔在了微任务队列中,当前的微任务队列中还有打印7的任务,所以最终是78的前边打印;