flutter async/await 详解

一、核心概念

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 是线程池调度驱动

相关推荐
步行cgn1 小时前
Spring Boot 中的 YAML 完全指南
android·spring boot·firefox
事圆则缓2 小时前
Java 常见数据结构与 Android 使用场景
android·java·数据结构
SoaringHeart2 小时前
Flutter 进阶 | 组件封装:用 CustomPainter 实现光环动画组件AnimatedHalo
前端·flutter
Light Gao2 小时前
企业级移动端 APP 架构设计:从原生双端到 Hybrid、React Native 与 Flutter
flutter·react native·react.js
RisunJan2 小时前
Android 高频面试题与解答
android
MyBili2 小时前
【安卓开发/搞机】快图浏览(QuickPic)技术解析:为何这款3MB应用仍是本地图库的性能天花板?
android·app·安卓·文件管理·相册·看图软件·手机相册
事圆则缓2 小时前
Kotlin 高阶工程化与 Android 深入实践
android·开发语言·kotlin
zhangphil3 小时前
AI大模型生成maxTokens 与上下文context
android·llama
淡淡的香烟3 小时前
Androidiot开发之猫脸识别
android·物联网