一、核心概念
1. 为什么需要 async/await?
Flutter 是单线程事件循环模型(类似 JavaScript),所有 UI 代码都在主线程(Isolate)执行。如果执行耗时操作(网络请求、文件读写、数据库查询),会阻塞 UI 导致卡顿。
async/await 是 Dart 提供的语法糖,让你用"同步的写法"处理"异步的逻辑",代码更易读。
2. 三个关键角色
| 概念 | 含义 | 类比 |
|---|---|---|
Future<T> |
表示将来会完成的异步操作,成功返回 T,失败抛异常 | Android 的 CompletableFuture |
async |
标记函数为异步函数,函数内部可以使用 await |
声明"这个函数里有异步操作" |
await |
等待一个 Future 完成,并获取其结果 | "在这里停下来,等结果回来再继续" |
二、基础用法
1. 定义异步函数
// async 函数必须返回 Future(或 void)
Future<String> fetchUserData() async {
// await 会暂停此处,等待 Future 完成,但不会阻塞 UI 线程
final response = await http.get(Uri.parse('https://api.example.com/user'));
if (response.statusCode == 200) {
return response.body; // 自动包装成 Future<String>
} else {
throw Exception('请求失败'); // 抛异常
}
}
2. 调用异步函数
void getData() async {
try {
print('开始请求...');
String data = await fetchUserData(); // 等待完成
print('收到数据: $data'); // 完成后执行
} catch (e) {
print('出错了: $e');
}
}
执行流程图解:
主线程: [print开始] → [发起请求] → [挂起,执行其他UI任务] → [请求完成] → [print收到数据]
↑___________________________________________|
await 期间,事件循环继续处理其他事件
三、与 Android 的对比
| 场景 | Android (Kotlin) | Flutter (Dart) |
|---|---|---|
| 异步结果 | suspend + Deferred |
async + Future |
| 等待结果 | val result = deferred.await() |
var result = await future |
| 线程切换 | withContext(Dispatchers.IO) |
自动在事件循环中调度,Isolate 间用 compute() |
| 并发执行 | async { } |
Future.wait([...]) |
四、进阶用法
1. 并行执行多个异步任务
// ❌ 错误:串行执行,总时间 = 3s + 3s + 3s = 9s
final a = await fetchA();
final b = await fetchB();
final c = await fetchC();
// ✅ 正确:并行执行,总时间 ≈ 3s
final results = await Future.wait([
fetchA(),
fetchB(),
fetchC(),
]);
// results = [a结果, b结果, c结果]
2. 超时控制
try {
final data = await fetchData().timeout(Duration(seconds: 5));
} on TimeoutException {
print('请求超时');
}
3. 延迟执行
await Future.delayed(Duration(seconds: 2)); // 延迟2秒
4. 同步执行异步代码(谨慎使用)
// 在 initState 等同步方法中需要异步数据
@override
void initState() {
super.initState();
// ❌ 不能直接用 await
// final data = await fetchData();
// ✅ 启动异步任务
_loadData();
}
void _loadData() async {
final data = await fetchData();
setState(() {
_data = data;
});
}
五、错误处理
1. try-catch 捕获异常
Future<void> loadData() async {
try {
final data = await fetchData();
} on SocketException {
print('网络错误');
} on FormatException {
print('数据格式错误');
} catch (e, stackTrace) {
print('未知错误: $e');
print(stackTrace); // 打印堆栈
} finally {
print('无论成败都会执行');
}
}
2. Future 的链式错误处理
fetchData()
.then((value) => print(value))
.catchError((e) => print('错误: $e'))
.whenComplete(() => print('结束'));
六、Flutter 中的典型场景
1. 网络请求 + UI 更新
class _MyPageState extends State<MyPage> {
List<User> _users = [];
bool _isLoading = false;
Future<void> _fetchUsers() async {
setState(() => _isLoading = true);
try {
final response = await http.get(Uri.parse('https://api/users'));
final List<dynamic> json = jsonDecode(response.body);
setState(() {
_users = json.map((e) => User.fromJson(e)).toList();
});
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('加载失败: $e')),
);
} finally {
setState(() => _isLoading = false);
}
}
}
2. 文件读写
Future<String> readFile() async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/data.txt');
return await file.readAsString();
}
3. SharedPreferences
Future<void> saveToken(String token) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', token);
}
七、常见坑
❌ 坑 1:在 build 方法里直接 await
// 错误!build 必须是同步的
@override
Widget build(BuildContext context) {
final data = await fetchData(); // ❌ 编译错误
return Text(data);
}
// 正确:用 FutureBuilder
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('错误: ${snapshot.error}');
}
return Text(snapshot.data!);
},
);
}
❌ 坑 2:忘记 await
// 错误:没有 await,函数会立即返回,不等待完成
void save() async {
final prefs = SharedPreferences.getInstance(); // 少了 await!
prefs.setString('key', 'value'); // 这里 prefs 是 Future<SharedPreferences>
}
// 正确
void save() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('key', 'value');
}
❌ 坑 3:setState 时 Widget 已销毁
Future<void> loadData() async {
final data = await fetchData();
// 如果用户在这时候退出了页面,setState 会报错
if (mounted) { // ✅ 先检查 Widget 是否还在树中
setState(() {
_data = data;
});
}
}
八、总结
| 要点 | 说明 |
|---|---|
async |
修饰函数,内部可用 await |
await |
等待 Future 完成,挂起当前函数但不阻塞线程 |
Future |
异步操作的"承诺",类似 Promise |
| 并行 | 用 Future.wait([]) |
| 错误 | 用 try-catch 或 .catchError() |
| UI 更新 | 异步操作后用 setState,记得检查 mounted |
async/await 的本质是 Dart 编译器帮你把代码转换成 Future 的 then/catchError 链式调用,让你写异步代码像写同步代码一样直观。
flutter的async /await 和 android 的 async / await 对比
这是一个非常好的深入问题。虽然两者表面上都叫 async/await,但 Flutter (Dart) 和 Android (Kotlin) 的实现机制、线程模型和设计理念差异巨大。下面从多个维度详细对比:
一、核心定位对比
| 维度 | Flutter (Dart) | Android (Kotlin) |
|---|---|---|
| 本质 | 单线程事件循环的语法糖 | 多线程协程的语法糖 |
| 底层机制 | Future + _SuspendState 状态机 |
Continuation + CPS 状态机 |
| 线程关系 | async/await 不创建线程,只在同一线程挂起/恢复 | async/await 可跨线程调度(Dispatchers) |
| 并发单元 | Future(将来值) |
Deferred<T>(可等待的协程任务) |
| 结构化并发 | ❌ 无内置作用域,需手动取消 | ✅ CoroutineScope 自动管理生命周期 |
二、语法写法对比
Dart (Flutter)
// async 函数返回 Future<T>
Future<String> fetchUser() async {
final response = await http.get(Uri.parse('/user')); // await 等待 Future
return response.body;
}
// 并行执行
Future<void> loadAll() async {
final results = await Future.wait([
fetchUser(),
fetchOrders(),
]);
}
Kotlin (Android)
// suspend 函数,不直接返回 Future/Deferred
suspend fun fetchUser(): String {
val response = api.getUser() // suspend 函数可直接调用
return response.body
}
// 并行执行(需要 CoroutineScope)
suspend fun loadAll() = coroutineScope {
val user = async { fetchUser() } // async 启动新协程,返回 Deferred
val orders = async { fetchOrders() }
val result = "${user.await()} + ${orders.await()}" // await 等待 Deferred
}
三、底层实现差异(关键!)
Dart:基于事件循环的状态机
Dart 的 async/await 编译后本质上还是 Future.then 的链式调用。
-
遇到
await时,函数被挂起 (suspend),当前状态保存到_SuspendState -
事件循环继续处理其他任务(UI 渲染、点击事件等)
-
Future 完成后,通过微任务(microtask)恢复执行
关键特点 :全程在**同一个 Isolate(单线程)**内完成,没有线程切换开销。
Kotlin:基于 CPS 的协程状态机
Kotlin 的 suspend 函数编译后会被改写成Continuation Passing Style:
// 你写的:
suspend fun getUser(): User?
// 编译后类似:
fun getUser(continuation: Continuation<*>): Any?
-
Continuation保存函数状态、局部变量、调用上下文 -
多个
Continuation形成链表,支持挂起和恢复 -
可以通过
withContext(Dispatchers.IO)自由切换线程
四、线程模型对比(最大区别!)
| 场景 | Dart (Flutter) | Kotlin (Android) |
|---|---|---|
| 默认执行线程 | 主 Isolate(单线程) | 调用方所在线程(通常是主线程) |
| IO 操作 | 单线程事件循环,IO 通过 C++ 层非阻塞实现 | withContext(Dispatchers.IO) 切换到 IO 线程池 |
| 耗时计算 | 会阻塞 UI!必须用 compute() 放到 Isolate |
withContext(Dispatchers.Default) 切换到计算线程池 |
| 线程切换 | ❌ async/await 本身不支持 | ✅ Dispatchers.Main/IO/Default 自由切换 |
形象比喻
-
Dart :像一家只有一个服务员的餐厅 。服务员(事件循环)接待客人 A,A 点菜(await),服务员去服务 B,菜好了(Future 完成)服务员回来继续服务 A。始终只有一个人。
-
Kotlin :像一家有多个服务员的餐厅 。主服务员(主线程)接待客人 A,A 需要做菜时,主服务员把任务转给厨房服务员(IO 线程),菜好了厨房服务员通知主服务员端给 A。可以有多个人协作。
五、并行执行对比
Dart:用 Future.wait
// 同时启动,等待全部完成
final results = await Future.wait([
fetchA(), // 返回 Future
fetchB(),
fetchC(),
]);
Kotlin:用 async + await
coroutineScope {
val a = async { fetchA() } // 立即启动,返回 Deferred
val b = async { fetchB() }
val c = async { fetchC() }
// 等待全部完成
val results = listOf(a.await(), b.await(), c.await())
}
⚠️ 注意 :Kotlin 的 async { }.await() 是串行的,async { } 单独调用才是并行的。
六、生命周期与取消机制
| 特性 | Dart | Kotlin |
|---|---|---|
| 作用域 | 无内置作用域概念 | lifecycleScope, viewModelScope |
| 自动取消 | ❌ 需手动管理(如 CancelableOperation) |
✅ 组件销毁时自动取消协程 |
| 结构化并发 | ❌ 子任务异常不会自动取消兄弟任务 | ✅ 一个子任务失败,兄弟任务自动取消 |
Kotlin 示例(结构化并发优势)
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// ViewModel 清除时自动取消,避免内存泄漏
val data = fetchData()
}
}
}
Dart 示例(需手动管理)
class _MyPageState extends State<MyPage> {
StreamSubscription? _sub;
@override
void dispose() {
_sub?.cancel(); // 必须手动取消
super.dispose();
}
}
七、错误处理对比
| 方式 | Dart | Kotlin |
|---|---|---|
| try-catch | ✅ 支持 | ✅ 支持 |
| 异常传播 | Future 异常需 await 才能捕获 | 协程异常自动向上传播,可集中处理 |
| SupervisorJob | 无对应概念 | 可隔离子协程异常,不影响兄弟协程 |
八、总结:什么时候用什么?
| 场景 | 推荐方案 |
|---|---|
| Flutter 开发 | 用 Dart async/await,记住它是单线程的 ,耗时计算用 compute() |
| Android 开发 | 用 Kotlin Coroutines,善用 Dispatchers.IO 做网络/数据库,Dispatchers.Default 做计算 |
| 跨平台逻辑层 | Kotlin Multiplatform 的协程可在 Android/iOS 共享,但 iOS 端是单线程模拟 |
一句话总结
Dart 的 async/await = 单线程事件循环里的"假装等待"(挂起/恢复) Kotlin 的 async/await = 多线程协程池里的"真正调度"(可跨线程)
两者都是语法糖,但背后的"糖衣"包裹的完全是不同的实现:Dart 是事件循环驱动 ,Kotlin 是线程池调度驱动。