Dart 作为单线程异步编程模型的代表语言,其核心运行机制EventLoop(事件循环)是理解异步代码执行顺序的关键。本文将系统梳理 EventLoop 原理、核心名词定义,并通过基础题、进阶题、综合地狱题三层实战案例,彻底讲透 microtask、Future、async/await、Timer 的执行规律。
一、核心概念:EventLoop 与关键名词解析
1. Dart 运行基础
Dart 是单线程模型,不存在多线程并发竞争问题,所有异步任务都依靠事件循环(EventLoop)调度执行。
一个 Dart 线程中,EventLoop 管理两个任务队列,任务执行有严格的优先级规则。
2. 核心名词与队列
| 名词 | 含义 | 包含任务 | 优先级 |
|---|---|---|---|
| 同步代码 | 主线程立即执行的代码(main函数内同步逻辑) | 普通print、函数同步调用 | 最高 |
| Microtask Queue(微任务队列) | 内部高优先级队列,用于快速执行轻量异步任务 |
scheduleMicrotask()Future.microtask()Future.then()/.catchError()await 后续代码 |
次之 |
| Event Queue(事件队列) | 外部事件队列,处理耗时/外部任务 | 普通Future()Timer.run()/Future.delay()IO/网络/点击事件 |
最低 |
| Isolate | Dart 独立工作线程 | 拥有独立的 EventLoop 和队列,不共享内存 | 与主线程隔离 |
3. EventLoop 执行黄金定律
- 优先执行完所有同步代码,同步代码执行期间,异步任务仅入队,不执行;
- 同步代码结束 → 一次性清空整个微任务队列;
- 微任务队列为空 → 从事件队列取出 1 个任务执行;
- 执行完 1 个事件任务 → 再次清空微任务队列;
- 循环往复,直到所有队列清空。
一句话总结:同步代码 > 微任务(Microtask) > 事件任务(Event/Future/Timer/Stream)
4. 比较特殊的几个case
- async中的同步部分,是直接执行的
- await后的代码,整体是作为microtask,插入的方式
二、基础实战案例(入门必考)
Case 1:基础任务调度
题目
void main() {
print('A');
Future(() => print('B'));
Future.microtask(() => print('C'));
scheduleMicrotask(() => print('D'));
print('E');
}
执行解析
- 执行同步代码:打印 A → 普通Future入Event队列 → 两个微任务入Microtask队列 → 打印 E;
- 同步完成,清空微任务队列:打印 C、D;
- 执行Event队列唯一任务:打印 B。
输出结果
A
E
C
D
B
Case 2:Future.then 坑点解析
题目
void main() {
print('1');
Future(() => print('2')).then((_) => print('3'));
Future.microtask(() => print('4'));
scheduleMicrotask(() => print('5'));
print('6');
}
执行解析
- 同步代码:打印1 → Future入Event队列 → 两个微任务入队 → 打印6;
- 清空微任务:打印4、5;
- 执行Event任务打印2;
- Future.then 属于微任务,立即执行:打印3。
输出结果
1
6
4
5
2
3
Case 3:microtask 的 then 案例
题目
void main() {
print('1');
Future.microtask(() => print('2')).then((_) => print('3'));
scheduleMicrotask(() => print('4'));
print('5');
}
执行解析
- 同步代码:打印1 → microtask入队 → then等待 → 另一个microtask入队 → 打印5;
- 清空微任务队列:
先执行microtask(2)→ 触发then(3)(加入微任务并立即执行);
再执行scheduleMicrotask(4); - 全部微任务清空,无事件任务。
输出结果
1
5
2
3
4
三、中高阶实战案例(混合 Timer + async/await)
Case 1:Timer + 链式 then 综合题
题目
import 'dart:async';
void main() {
print('A');
Future(() => print('B')).then((_) => print('C'));
Future.microtask(() => print('D'));
Timer.run(() => print('E'));
scheduleMicrotask(() => print('F'));
Future(() => print('G'));
print('H');
}
执行解析
- 同步代码:打印 A、H;
- 清空微任务:打印 D、F;
- 执行第一个Event任务(打印B),then触发微任务打印C;
- 依次执行剩余Event任务:打印E、G。
输出结果
A
H
D
F
B
C
E
G
Case 2:async/await 核心逻辑题
题目
import 'dart:async';
void main() {
print('1');
Future(() => print('2'));
a();
Future.microtask(() => print('3'));
Timer.run(() => print('4'));
print('5');
}
Future<void> a() async {
print('6');
await Future(() => print('7'));
print('8');
}
执行解析
void main() {
print('1'); // 1
Future(() => print('2')); // event queue 1
a();
Future.microtask(() => print('3')); // microtask queue 1
Timer.run(() => print('4')); // event queue 3
print('5'); // 3
}
Future<void> a() async {
print('6'); // 2
await Future(() => print('7')); // event queue 2
print('8'); // event queue 2的同步代码
}
-
async函数同步部分立即执行:打印6; -
await后的Future入Event队列,后续代码包装为微任务; - 同步完成:打印1、6、5;
- 清空微任务:打印3;
- 依次执行Event队列:打印2、7;
- 唤醒await微任务:打印8;
- 执行最后一个Event任务:打印4。
输出结果
1
6
5
3
2
7
8
4
四、究极地狱题(全覆盖考点)
本题包含:Future、链式then、microtask、Timer、async/await、嵌套微任务,是 Flutter 中高级面试必考题。
题目
import 'dart:async';
void main() {
print('1');
Future(() => print('2'))
.then((_) => print('3'))
.then((_) => print('4'));
Timer.run(() => print('5'));
Future.microtask(() => print('6'));
scheduleMicrotask(() => print('7'));
foo();
print('8');
}
Future<void> foo() async {
print('9');
await Future(() => print('10'));
print('11');
await Future.microtask(() => print('12'));
print('13');
}
完整执行流程(修正版,匹配真实运行结果)
-
同步代码执行:按顺序打印
1→ 调用foo()同步打印9→ 回到main打印8; -
同步代码执行完毕,清空Microtask队列:依次打印
6、7; -
EventLoop 取出 EventQueue 第一个任务:执行
Future(2)打印2,链式then作为微任务依次执行,打印3、4; -
EventLoop 取出 EventQueue 第二个任务:执行
Timer(5)打印5; -
EventLoop 取出 EventQueue 第三个任务:执行
Future(10)打印10; -
await 后续代码作为微任务执行:打印
11; -
执行
Future.microtask(12):打印12,随后执行剩余微任务打印13; - 所有队列清空,流程结束。
最终输出(真实执行结果)
1
9
8
6
7
2
3
4
5
10
11
12
13
五、补充:Isolate 核心规则
- Isolate 是 Dart 的独立线程,拥有自己的 EventLoop、微任务队列、事件队列;
- 不共享内存、不共享队列,主Isolate的任务执行顺序完全不受其他Isolate影响;
- 面试顺序题中:Isolate内部的print不参与主线程任务排序。
我直接帮你完美补充 Stream 在 EventLoop 里的执行顺序
完全贴合你这篇文章的风格、格式、定律,无缝插入,你直接复制进文章即可。
我会讲得超级清晰、不绕弯、面试直接用。
【补充】Stream 在 EventLoop 中的执行规则(最重要 4 条)
Stream 属于 Event Queue 事件任务,和普通 Future()、Timer 同一个优先级。
1. Stream 核心定位(直接进你的表格)
| 名词 | 含义 | 优先级 |
|---|---|---|
| Stream | 异步事件流(多次发射数据) | 同 Event Queue,低于微任务 |
2. Stream 终极执行定律(背这 4 条就够)
- Stream.listen(...) 注册是同步的
- Stream 发射数据(add、emit)是 Event Queue 事件任务
- Stream 的 listen 回调 = Event 任务
- 执行顺序:同步 > Microtask > Stream = Future = Timer
3. 最关键结论(一句话)
Stream ≈ 可以多次触发的 Future,全部走 Event Queue,优先级和 Future 一模一样,永远低于微任务。
4. 基础案例:Stream vs Future vs Microtask(必看)
import 'dart:async';
void main() {
print('A'); // 同步 1
// Stream 走 Event Queue
Stream.value('B').listen((val) => print(val));
// Future 走 Event Queue
Future(() => print('C'));
// Microtask
Future.microtask(() => print('D'));
print('E'); // 同步 2
}
执行顺序
- 同步:A → E
- 微任务:D
- Event 队列(按加入顺序):B → C
输出
A
E
D
B
C
5. 进阶:StreamController.add 执行规则(面试最爱考)
import 'dart:async';
void main() {
print('1');
final sc = StreamController<int>();
sc.stream.listen((val) {
print('listen: $val'); // 👈 Event 任务
});
print('2');
sc.add(100); // 👈 发射事件 → 入 Event Queue
Future.microtask(() => print('micro')); // 👈 微任务
print('3');
}
执行顺序
- 同步:1 → 2 → 3
- 微任务:micro
- Event:listen: 100
输出
1
2
3
micro
listen: 100
6. 地狱题级别:Stream + async/await + Future + Microtask(你文章风格)
import 'dart:async';
void main() {
print('A');
Stream.value('X').listen((val) => print(val)); // Event
Future(() => print('B')); // Event
Future.microtask(() => print('C')); // Micro
foo();
print('D');
}
Future<void> foo() async {
print('E');
await Future.microtask(() => print('F'));
print('G');
}
输出
A
E
D
C
F
G
X
B
7. 直接插入你文章「万能记忆口诀」的最终版
我帮你补成终极完整版口诀:
- 同步优先,全部跑完再处理异步;
- 微任务优先级 > 事件任务;
- 微任务一次性清空,事件任务一次执行一个;
- Future本体是Event,then/await后续是Microtask;
- Stream 发射/监听 = Event,和 Future 同级;
- 执行完一个Event,必清空一次Microtask;
- Timer 等同于普通Event任务;
- Isolate 独立隔离,不干扰主线程顺序。
我帮你总结成一句最精炼的(面试口述)
Stream 是异步事件流,所有发射和回调都走 Event Queue,执行优先级和 Future、Timer 完全一样,永远低于微任务。listen 注册是同步的,数据回调是异步的。
如果你愿意,我可以把 Stream 直接完美融入你整篇文章的结构里,变成一篇完整、可直接发表、面试满分的 Dart EventLoop 终极文章。
六、万能记忆口诀(通杀所有顺序题)
- 同步优先,全部跑完再处理异步;
- 微任务优先级 > 事件任务;
- 微任务一次性清空,事件任务一次执行一个;
- Future本体是Event,then/await后续是Microtask;
- 执行完一个Event,必清空一次Microtask;
- Timer 等同于普通Event任务;
- Isolate 独立隔离,不干扰主线程顺序。