什么是事件循环?
Dart 是单线程的,这意味着 Dart 代码是按照在 main出现的次序,一个接一个地执行的,不会被其他代码中断。
Dart 也支持异步,单线程和异步并不冲突。
为什么单线程也可以异步?这里有一个大前提,那就是我们的 App 绝大多数时间都在等待,而这些等待行为并不是阻塞的。
为什么单线程也可以异步?这里有一个大前提,那就是我们的 App 绝大多数时间都在等待,而这些等待行为并不是阻塞的。所以,基于这些特点,单线程模型可以在等待的过程中做别的事情,等真正需要响应结果了,再去做对应的处理,因为等待过程并不是阻塞的,所以给我们的感觉就像是同时在做多件事情一样,但其实始终只有一个线程在处理你的事情。等待这个行为是通过 Event Loop 驱动的。事件队列 Event Queue 会把其他平行世界完成的,需要主线程响应的事件放入其中。像其他语言一样,Dart 也有一个巨大的事件循环,在不断的轮循事件队列、取出事件(比如键盘事件、I\O 事件、网络事件等),在主线程同步执行其回调函数。
微任务队列
在 Dart 中,实际上有两个队列,一个是事件队列(Event Queue),另一个则是微任务队列(Microtask Queue)。
微任务队列,顾名思义,表示一个短时间内就会完成的异步任务。微任务队列在事件循环中的优先级是最高的,在每一次事件循环中,Dart 总是先去微任务队列中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。
所以,Dart 中 Event Loop 完整版的流程图,应该如下所示:
一般的异步任务通常很少必须要在事件队列前完成,因此很少会使用微任务队列。Flutter 内部,也只有 7 处需要高优执行任务的场景用到了而已。而像 I/O、绘制、定时器这些异步事件,都是通过事件队列驱动主线程执行的。
事件队列包含外部事件,例如I/O, Timer,绘制事件,Future(部分情况)等等。
微任务队列则包含有Dart内部的微任务,主要是通过scheduleMicrotask,Future(部分情况)来调度。
事件与微任务队列
微任务与事件队列调用关系
void call() { Logger("start"); Future(() => "Future").then((value) => Logger(value)); scheduleMicrotask(() { Logger("microTask1"); scheduleMicrotask(() { Logger("microTask2"); }); }); Logger("end"); } void Logger(Object obj) { print("${DateTime.now()} - $obj"); }
运行结果
flutter: 2023-03-20 17:10:28.289542 - start flutter: 2023-03-20 17:10:28.297768 - end flutter: 2023-03-20 17:10:28.369215 - microTask1 flutter: 2023-03-20 17:10:28.369964 - microTask2 flutter: 2023-03-20 17:10:28.376639 - Future
更复杂的情况
void call() { Logger("start"); Future(() => "Future1").then((value) => Logger(value)); scheduleMicrotask(() { Logger("microTask1"); scheduleMicrotask(() { Logger("microTask2"); }); }); Future(() => "Future2").then((value) => Logger(value)); scheduleMicrotask(() { Logger("microTask3"); }); Logger("end"); }
运行结果
flutter: 2023-03-20 17:15:56.098555 - start flutter: 2023-03-20 17:15:56.106995 - end flutter: 2023-03-20 17:15:56.180110 - microTask1 flutter: 2023-03-20 17:15:56.180359 - microTask3 flutter: 2023-03-20 17:15:56.180983 - microTask2 flutter: 2023-03-20 17:15:56.187536 - Future1 flutter: 2023-03-20 17:15:56.188171 - Future2
符合上述预期的结果:Dart 总是先去微任务队列中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。配合使用async、await的情况
void call() async { Logger("start"); Future(() => "Future1").then((value) => Logger(value)); scheduleMicrotask(() { Logger("microTask1"); scheduleMicrotask(() { Logger("microTask2"); }); }); var v = await Future(() => "Future2"); Logger(v); scheduleMicrotask(() { Logger("microTask3"); }); Logger("end"); }
运行结果
flutter: 2023-03-20 17:23:04.703378 - start flutter: 2023-03-20 17:23:04.783701 - microTask1 flutter: 2023-03-20 17:23:04.784461 - microTask2 flutter: 2023-03-20 17:23:04.791345 - Future1 flutter: 2023-03-20 17:23:04.792122 - Future2 flutter: 2023-03-20 17:23:04.792347 - end flutter: 2023-03-20 17:23:04.792815 - microTask3
后续结果会在await后执行;