Flutter 事件机制 - Future 和 MicroTask 全解析

7,445 阅读6分钟

写在前面

了解过Flutter的同学都知道,不同于 Android 原生开发,dart 是单线程实体的语言,所以我们一般的异步操作,实际上还是通过单线程通过调度任务优先级来实现的,就是我们经常用到的 Future,但是Flutter中的事件机制究竟是怎样的?多个Future 和 Microtask 程序的执行顺序是怎样的? 本文将借助两个比较复杂的例子来详细介绍 Flutter 的事件机制,希望能对大家有所帮助。

Main代码块,EventQueue,MicrotaskQueue 的执行优先级

首先我得提一嘴 isolate(隔离),isolate是有自己的内存和单线程控制的运行实体,isolate类似于线程。 运行中的 Flutter 程序由一个或多个 isolate 组成。我们的代码默认都在 Main isolate中执行。
为了保持高的响应性,特别耗时的任务一般不要放在Main isolate 中。但 isolate 不是本文的重点,在此就不过多赘述。

Dart 中事件机制的实现 :Main isolate 中有一个Looper,但存在两个Queue:Event Queue 和 Microtask Queue 。
因为 isolate 是单线程实体,所以 isolate中的代码是按顺序执行的。
所以 dart 中的代码执行优先级可以分为三个级别:

  1. 在 Main 中写代码将最先执行;
  2. 执行完 Main 中的代码,然后会检查并执行 Microtask Queue 中的任务, 通常使用 scheduleMicrotask 将事件添加到 MicroTask Queue 中;
  3. 最后执行 EventQueue 队列中的代码,通常使用 Future 向 EventQueue加入时间,也可以使用 async 和 await 向 EventQueue 加入事件。

总结:Dart 中事件的执行顺序:Main > MicroTask > EventQueue。
如图:

验证:

void testSX(){
  new Future(() => print('s_1'));
  scheduleMicrotask(() => print('s_2'));
  print('s_3');
}

输出结果:

I/flutter (32415): s_3
I/flutter (32415): s_2
I/flutter (32415): s_1

Future简介

前面讲到,用 async 和 await 组合,即可向 event queue 中插入 event 实现异步操作,那为什么还会有Future呢?
其实,Future 最主要的功能就是提供了链式调用。

new Future (() => print('拆分任务_1'))
    .then((i) => print('拆分任务_2'))
    .then((i) => print('拆分任务_3'))
    .whenComplete(()=>print('任务完成'));

Future中的 then 并没有创建新的Event丢到Event Queue中,而只是一个普通的Function,在一个 Future 所有的 Function 执行完后,下一个 Future 才会开始执行。

多个 Future 的执行顺序

  1. 规则一:Future 的执行顺序为Future的在 EventQueue 的排列顺序。类似于 JAVA 中的队列,先来先执行。
  2. 规则二:当任务需要延迟执行时,可以使用 new Future.delay() 来将任务延迟执行。
  3. 规则三: Future 如果执行完才添加 than ,该任务会被放入 microTask,当前 Future 执行完会执行 microTask,microTask 为空后才会执行下一个Future。
  4. 规则四:Future 是链式调用,意味着Future 的 then 未执行完,下一个then 不会执行。

理论结束,然后来看一段代码吧:

void testFuture() {
  Future f1 = new Future(() => print('f1'));
  Future f2 = new Future(() =>  null);
  Future f3 = new Future.delayed(Duration(seconds: 1) ,() => print('f2'));
  Future f4 = new Future(() => null);
  Future f5 = new Future(() => null);

  f5.then((_) => print('f3'));
  f4.then((_) {
    print('f4');
    new Future(() => print('f5'));
    f2.then((_) {
      print('f6');
    });
  });
  f2.then((m) {
    print('f7');
  });
  print('f8');
}

各位同学可以试着写一下结果,然后对比下输出结果。

输出结果:

com.example.flutter_dart_app I/flutter: f8
com.example.flutter_dart_app I/flutter: f1
com.example.flutter_dart_app I/flutter: f7
com.example.flutter_dart_app I/flutter: f4
com.example.flutter_dart_app I/flutter: f6
com.example.flutter_dart_app I/flutter: f3
com.example.flutter_dart_app I/flutter: f5
com.example.flutter_dart_app I/flutter: f2

是不是跟自己的结果大相径庭,别急,看我来慢慢分析: 分析:

  1. 首先执行Main 的代码,所以首先输出: 8;
  2. 然后参考上面的规则1,Future 1 到 5 是按初始化顺序放入 EventQueue中,所以依次执行Future 1到5 , 所以输出结果:8,1,7。
  3. 参考规则2,f3 延时执行,一定是在最后一个:8,1,7,…,2。
  4. 在 f4 中,首先输出 f4 :8,1,7,4,…,2。
  5. 在 f4 的 then 的方法块中,新建了Future, 所以新建的 Future 将在 EventQueue尾部,最后被执行:8,1,7,4,…,5,2。
  6. 在 f4 的 then 的方法块中,给 f2 添加了 then ,但此时 f2 已经执行完了,参考规则三,所以 then 中的代码会被放到 microTask 中,在当前 Future 执行完后执行。 因为此时Future f4已经执行完了,所以会处理microTask(microTask优先级高)。结果:8,1,7,4,6,..,5,2。
  7. 此时我们的 EventQueue 中还有 f5,和在 f4 中添加的新的Future。 所以我们的最终结果就是:8,1,7,4,6,3,5,2。

是不是有点理解不了,没事,牢记四个规则,自己再算一遍,相信你就了然于胸了。重要要在脑海里有一个 EventQueue 的队列模型,牢记先进先出。

然后来试一试下一题:

多Future 和 多micTask 的执行顺序

void testScheduleMicrotatsk() {
  scheduleMicrotask(() => print('Mission_1'));

//注释1
  new Future.delayed(new Duration(seconds: 1), () => print('Mission_2'));

//注释2
  new Future(() => print('Mission_3')).then((_) {
    print('Mission_4');
    scheduleMicrotask(() => print('Mission_5'));
  }).then((_) => print('Mission_6'));

//注释3
  new Future(() => print('Mission_7'))
      .then((_) => new Future(() => print('Mission_8')))
      .then((_) => print('Mission_9'));

//注释4
  new Future(() => print('Mission_10'));

  scheduleMicrotask(() => print('Mission_11'));

  print('Mission_12');
}

大家可以先自己试一下,再对照结果~

输出结果:

I/flutter (19025): Mission_12
I/flutter (19025): Mission_1
I/flutter (19025): Mission_11
I/flutter (19025): Mission_3
I/flutter (19025): Mission_4
I/flutter (19025): Mission_6
I/flutter (19025): Mission_5
I/flutter (19025): Mission_7
I/flutter (19025): Mission_10
I/flutter (19025): Mission_8
I/flutter (19025): Mission_9
Syncing files to device MIX 3...
I/flutter (19025): Mission_2

是不是还是没答全对?没关系,很正常,看我慢慢道来:
分析:

  1. 根据 Main > MicroTask > EventQueue。我们首先会得到输出结果:12,1,11。
  2. 注释1 的 Future 是延时执行,所以:12,1,11,…,2。
  3. 注释2 中创建了 Microtask,Microtask会在该Future执行完后执行,所以:12,1,11,4,6,5,…,2。
  4. 重点来了。我们在注释3 的Future 的 then 中新建了Future(输出Mission_8),新建的 Future 将被加到 EventQueue尾部,并且,注释3的Future后续的then将不再执行,因为这个链被阻塞了!
    注意对比上一题中的 f4, 上一题中的 f4 是一个 than 方法包裹了代码块。
    此时的结果:12,1,11,4,6,5,7,…,2。
  5. 执行完注释4 的 Future,然后会执行我们在注释3 Future 新加入的 Future,之后注释3 的Future不再阻塞,会继续执行,结果: 12,1,11,4,6,5,7,10,8,9,2。

看到这里,相信各位同学已经对 Dart 事件机制有一个大概的了解,希望能对 各位在学Flutter 的同学有所帮助,蟹蟹~

END

我是雷加,如果您喜欢我的文章,请留下你的赞;如有疑问和建议,请在评论区留言
我的 Github, 欢迎关注~