Dart 的异步模型看起来抽象,真正难点其实不在名词,而在弄清它解决了什么问题、靠什么机制运转。本文会从单线程的局限讲起,再拆开事件循环与两类任务队列的执行顺序,最后结合常见误区说明什么时候该用微任务、什么时候该考虑 Isolate,帮助你判断异步代码为什么会这样执行。
为什么 Dart 需要异步模型
Dart 引入异步模型,核心是同时解决两件事:一是程序里确实存在大量耗时操作,二是 Dart 本身采用单线程执行模型。
异步要解决哪些耗时场景
所谓异步操作,本质上是让耗时任务不要把后续代码全部卡住。也就是说,某个操作还没完成时,程序可以先继续往下执行,而不是原地等待。
常见的耗时操作主要包括:
- 网络请求:如 Http 请求。
- 文件操作:如读写文件、数据库操作。
- 图像处理:如格式处理。
如果这些任务都按同步方式串行等待,界面响应、逻辑调度和整体吞吐都会明显变差。
单线程模型的局限在哪里
Dart 的单线程意味着任务默认按顺序执行,并且同一时刻只能在主线程上处理一件事。这样的设计有明确好处,但也有天然边界。
它的局限主要体现在两点:
- 不能充分利用资源:单线程只使用一核 CPU,无法直接吃满多核能力。
- 耗时任务会阻塞整个线程:一旦当前代码里出现耗时操作,后面的任务就只能继续等待。
也正因为如此,Dart 需要一套机制,让耗时操作不直接卡住主线程,同时又不把语言模型变成传统多线程那种高同步成本的形态。
Dart 异步模型是怎么工作的
Dart 的异步模型建立在事件循环 + 任务队列之上。简单说,耗时 I/O 可以交给操作系统去执行,主线程不必一直阻塞等待;而当结果返回后,事件循环再负责把对应回调安排回主线程执行。
这也是 Dart 在保持单线程编程体验的同时,实现异步并发的关键。
先理解:Dart 仍然是单线程语言
Dart 是单线程语言,所有任务默认都由主线程,也就是主 Isolate 负责执行。
这种选择的直接优势是:顺序执行的语义更稳定,也天然避开了多线程常见的资源竞争、锁以及死锁问题。换句话说,Dart 没有靠共享内存多线程去解决大部分并发问题,而是优先通过事件循环来组织任务。
Dart 如何在单线程前提下实现并发和并行,可以分开理解:
- 并发:通过事件循环机制实现异步调度。
- 并行:通过开辟多个Isolate实现并行计算。
前者解决的是“主线程如何不被轻易阻塞”,后者解决的是“如何利用多核去跑真正适合并行的任务”。
事件循环与两类任务队列
Dart 的事件循环负责调度和执行任务。它并不是让多个任务在同一时刻真的一起跑,而是通过交错安排执行顺序,实现单线程下的并发效果。
这个模型里最关键的是两类任务队列:
- 微任务队列:优先级更高,适合放短小、需要尽快执行的后续任务。
- 事件任务队列:优先级低于微任务,常见的异步事件会排在这里等待处理。
调度规则很直接:优先级高的先执行。
真正需要记住的是循环执行规则:
- 先检查微任务队列是否为空。
- 如果不为空,先把微任务队列中的任务全部执行完。
- 微任务清空后,再从事件任务队列里取出第一个事件任务执行。
- 当前事件任务执行结束后,再重新回到第一步,继续检查微任务队列。
- 当两个队列都没有任务时,事件循环进入等待状态,直到有新事件到来。
这意味着一个重要事实:事件任务并不是一到就立刻执行,它必须等当前轮次的微任务全部处理完,才有机会进入执行阶段。
void eventLoop() {
while (true) {
if (微任务队列不为空) {
执行微任务队列中所有微任务;
} else if (事件队列不为空) {
处理事件任务队列中的第一个事件任务;
} else {
等待新的事件;
}
}
}
可以把它理解成一种固定节奏:先清空高优先级任务,再处理一个低优先级事件,然后重新判断。Dart 的异步行为之所以常常“看起来和直觉不一致”,大多就是因为代码实际进入的是不同队列。
使用 Dart 异步模型时要注意什么
理解事件循环之后,更重要的是知道哪些认识是对的,哪些是常见误解。

微任务优先级高,不等于执行更快
微任务并不是“性能更高”的任务,它只是优先级更高。因此它会先于事件任务执行,但这不代表它更适合承载耗时工作。
通常微任务应该是比较小、比较快的任务。如果把耗时逻辑塞进大量微任务里,就会导致事件任务长期得不到执行,也就是常说的“事件任务饥饿”。一旦出现这种情况,程序响应会变差,异步调度反而会失去意义。
同步耗时代码仍然会阻塞事件循环
很多人以为只要项目里用了异步模型,就不会再卡线程。实际上不是这样。
如果同步代码里本身存在耗时操作,例如某些 I/O 密集型逻辑,事件循环一样会被阻塞。原因很简单:同步代码会先执行,而主线程一旦被这段逻辑占住,后续的微任务和事件任务都无法被调度。
遇到这类问题,通常有两种处理思路:
- 如果任务可以拆开,就拆分为多个较小任务,再交给微任务调度。
- 如果任务不适合拆分,或者本身就是计算量较大的工作,则考虑使用 Isolate 做并行计算。
这个判断很关键,因为异步模型解决的是“调度问题”,不是把所有重任务自动变轻。
性能与 Isolate 的边界怎么理解
Dart 虽然是单线程,但并不意味着性能一定弱。文中给出的测试数据是:Dart VM 可每秒处理 20 多万个微任务事件,事件任务处理也能满足常规需求。
因此,大多数业务场景下,不需要因为“单线程”三个字就过度担心性能。真正需要关注的是任务类型是否放对位置:
- 短小、需要快速衔接的逻辑,适合微任务。
- 常规异步事件,按事件队列调度即可。
- 无法拆分的重计算或希望利用多核 CPU 的任务,则应考虑多个 Isolate。
换句话说,Dart 不是不能利用多核,而是默认执行模型不直接依赖多线程共享状态;当你确实需要并行能力时,可以通过开辟多个 Isolate 来完成。
总结:把握执行顺序,异步模型就不再抽象
Dart 异步模型的理解重点,不在背概念,而在抓住三件事。
- Dart 之所以需要异步,是因为耗时操作真实存在,而单线程会被阻塞。
- Dart 通过事件循环 + 任务队列组织异步执行,其中微任务队列优先级高于事件任务队列。
- 异步不等于万能提速。同步耗时代码仍会卡住主线程;可拆分任务可分批调度,不可拆分的重任务则更适合交给 Isolate。
只要你能判断一段代码进入了哪个队列、什么时候会轮到它执行、是否会阻塞主线程,Dart 的异步行为基本就能解释清楚了。









