ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

flutter async/await 详解

2026/9/5 5:09:01 拓冰建站 浏览量
flutter async/await 详解 一、核心概念1. 为什么需要 async/awaitFlutter 是单线程事件循环模型类似 JavaScript所有 UI 代码都在主线程Isolate执行。如果执行耗时操作网络请求、文件读写、数据库查询会阻塞 UI 导致卡顿。async/await是 Dart 提供的语法糖让你用同步的写法处理异步的逻辑代码更易读。2. 三个关键角色概念含义类比FutureT表示将来会完成的异步操作成功返回 T失败抛异常Android 的CompletableFutureasync标记函数为异步函数函数内部可以使用await声明这个函数里有异步操作await等待一个 Future 完成并获取其结果在这里停下来等结果回来再继续二、基础用法1. 定义异步函数// async 函数必须返回 Future或 void FutureString fetchUserData() async { // await 会暂停此处等待 Future 完成但不会阻塞 UI 线程 final response await http.get(Uri.parse(https://api.example.com/user)); if (response.statusCode 200) { return response.body; // 自动包装成 FutureString } 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)异步结果suspendDeferredasyncFuture等待结果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 捕获异常Futurevoid 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 StateMyPage { ListUser _users []; bool _isLoading false; Futurevoid _fetchUsers() async { setState(() _isLoading true); try { final response await http.get(Uri.parse(https://api/users)); final Listdynamic 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. 文件读写FutureString readFile() async { final directory await getApplicationDocumentsDirectory(); final file File(${directory.path}/data.txt); return await file.readAsString(); }3. SharedPreferencesFuturevoid 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 FutureBuilderString( 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 是 FutureSharedPreferences } // 正确 void save() async { final prefs await SharedPreferences.getInstance(); await prefs.setString(key, value); }❌ 坑 3setState 时 Widget 已销毁Futurevoid loadData() async { final data await fetchData(); // 如果用户在这时候退出了页面setState 会报错 if (mounted) { // ✅ 先检查 Widget 是否还在树中 setState(() { _data data; }); } }八、总结要点说明async修饰函数内部可用awaitawait等待 Future 完成挂起当前函数但不阻塞线程Future异步操作的承诺类似 Promise并行用Future.wait([])错误用try-catch或.catchError()UI 更新异步操作后用setState记得检查mountedasync/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将来值DeferredT可等待的协程任务结构化并发❌ 无内置作用域需手动取消✅CoroutineScope自动管理生命周期二、语法写法对比Dart (Flutter)// async 函数返回 FutureT FutureString fetchUser() async { final response await http.get(Uri.parse(/user)); // await 等待 Future return response.body; } // 并行执行 Futurevoid 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()放到 IsolatewithContext(Dispatchers.Default)切换到计算线程池线程切换❌ async/await 本身不支持✅Dispatchers.Main/IO/Default自由切换形象比喻Dart像一家只有一个服务员的餐厅。服务员事件循环接待客人 AA 点菜await服务员去服务 B菜好了Future 完成服务员回来继续服务 A。始终只有一个人。Kotlin像一家有多个服务员的餐厅。主服务员主线程接待客人 AA 需要做菜时主服务员把任务转给厨房服务员IO 线程菜好了厨房服务员通知主服务员端给 A。可以有多个人协作。五、并行执行对比Dart用Future.wait// 同时启动等待全部完成 final results await Future.wait([ fetchA(), // 返回 Future fetchB(), fetchC(), ]);Kotlin用asyncawaitcoroutineScope { 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 { }单独调用才是并行的。六、生命周期与取消机制特性DartKotlin作用域无内置作用域概念lifecycleScope,viewModelScope自动取消❌ 需手动管理如CancelableOperation✅ 组件销毁时自动取消协程结构化并发❌ 子任务异常不会自动取消兄弟任务✅ 一个子任务失败兄弟任务自动取消Kotlin 示例结构化并发优势class MyViewModel : ViewModel() { fun loadData() { viewModelScope.launch { // ViewModel 清除时自动取消避免内存泄漏 val data fetchData() } } }Dart 示例需手动管理class _MyPageState extends StateMyPage { StreamSubscription? _sub; override void dispose() { _sub?.cancel(); // 必须手动取消 super.dispose(); } }七、错误处理对比方式DartKotlintry-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 是线程池调度驱动。