Future在flutter的使用
如果你有具体场景(比如“列表页下拉刷新 + 并发请求 + 超时兜底”或“串行步骤向导、每步依赖上一步结果”),我可以按场景给一份更贴近业务的代码模板。下面把 Dart/Flutter 里的 Future 体系梳理一遍,配上常用写法、组合方式、在 Flutter UI 中的使用和一些坑位提示。Future<T> 表示“将来某个时刻返回一个 T,或者抛出一个错误”,只完成一次(单次异步结果)。UI 层
下面把 Dart/Flutter 里的 Future 体系梳理一遍,配上常用写法、组合方式、在 Flutter UI 中的使用和一些坑位提示。
一、Future 是什么
-
Future<T> 表示“将来某个时刻返回一个 T,或者抛出一个错误”,只完成一次(单次异步结果)。
-
状态流转:未完成 → 已完成(有值) 或 已完成(错误)。
-
与 Stream 的区别:Future 只产生一个值;Stream 可以产生多个值。
二、如何创建 Future
-
使用 async 函数(最常见)
Future<int> loadCount() async { await Future.delayed(const Duration(milliseconds: 300)); return 42; // 如果 throw,则以错误完成 }
-
工具构造
Future.value(42); // 直接完成 Future.error(Exception('oops')); // 以错误完成 Future.delayed(Duration(seconds: 1), () => 'done'); // 延时后返回 Future.microtask(() => heavyCalc()); // 微任务队列 Future.sync(() => maybeSync()); // 同步执行并包装为 Future Future(() => computeNow()); // 放入事件队列异步执行
-
Completer(桥接回调式 API)
final completer = Completer<String>(); someCallbackAPI((result, err) { if (err != null) completer.completeError(err); else completer.complete(result); }); return completer.future;
-
CPU 密集型任务不要用 Future 直接在主 Isolate 跑,避免卡 UI。用 compute 或 Isolate:
// 适合较简单的耗时函数(顶级/静态函数) final result = await compute(parseJson, jsonString); // 或者更灵活地使用 Isolate(适合特别重的任务) // final result = await Isolate.run(() => heavyWork());
三、如何使用 Future(消费结果)
-
async/await(可读性最好)
try { final user = await fetchUser(); // ... } catch (e, st) { // 统一异常处理 / 上报 } finally { // 清理收尾 }
-
链式 API
fetchUser() .then((u) => save(u)) .catchError((e) => logError(e)) .whenComplete(() => print('done'));
-
超时控制
final data = await fetchData() .timeout(const Duration(seconds: 5), onTimeout: () => 'fallback');
四、组合多个 Future
-
并发执行(并行)
final results = await Future.wait([ fetchA(), fetchB(), fetchC(), ]); // 返回顺序与传入顺序一致
-
取最先完成的一个
final first = await Future.any([fetchFast(), fetchSlow()]);
-
顺序执行(逐个等待)
for (final url in urls) { final html = await fetch(url); // 依赖上一个结果的场景 } // 或 Future.forEach(urls, (u) => fetch(u));
五、错误处理要点
-
async/await 下用 try/catch 即可捕获 Future 抛出的错误。
-
then/catchError 链条:catchError 只捕获链上之前的错误,注意链式位置。
-
不中断但要“忽略”的 Future(不 await)要自己加错误兜底,避免未处理异常泄漏到 Zone:
() async { try { await fireAndForget(); } catch (e) { logError(e); } }();
-
顶层兜底(需要时):runZonedGuarded 或 FlutterError.onError/PlatformDispatcher.instance.onError。
六、在 Flutter 里的使用(FutureBuilder 与状态管理)
-
FutureBuilder(展示加载/错误/完成三态)
class UserPageState extends State<UserPage> { late final Future<User> _future; @override void initState() { super.initState(); _future = fetchUser(); // 别在 build 里新建 Future,避免重复请求 } @override Widget build(BuildContext context) { return FutureBuilder<User>( future: _future, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('出错:${snapshot.error}')); } final user = snapshot.data!; return Text('Hello, ${user.name}'); }, ); } }
-
setState 与异步的典型坑:组件已销毁但异步回调里还在 setState
Future<void> _load() async { final data = await fetch(); if (!mounted) return; // 或 context.mounted setState(() => _data = data); }
-
不要在 build 方法里直接发起异步请求(会在每次重建时重复触发)。放到 initState、事件回调,或用状态管理工具(Provider/Bloc/Riverpod 等)托管。
七、调度与事件循环(简要)
-
Dart 有微任务队列与事件队列:
-
Future.microtask/scheduleMicrotask 加到微任务队列,优先于事件队列执行。
-
Future(...) 和 I/O 回调属于事件队列。
-
-
这影响到代码执行顺序和 UI 响应时机,但实际业务中只需记住:不要阻塞主线程;耗时的 CPU 任务丢到 Isolate。
八、可取消吗?
-
Future 本身不可取消;常见策略:
-
用标志位/请求 token 忽略过期结果;
-
第三方的 CancelableOperation(package:async);
-
I/O 层支持的取消(如 http 请求的 close/abort);
-
对于 Isolate,直接 kill 该 isolate。
-
九、测试提示
-
Widget 测试里异步通常用:
await tester.pump(); // 推进一帧 await tester.pumpAndSettle(); // 等待所有动画/微任务完成
-
纯 Dart 测试可用 fakeAsync 控制时间推进,或直接 await。
十、常见最佳实践速查
-
UI 层尽量用 async/await,库/工具层可用 then/catchError。
-
并发就用 Future.wait;顺序依赖就按顺序 await。
-
需要 loading/错误界面就用 FutureBuilder,且缓存 Future,避免重复请求。
-
try/catch 包裹 await,finally 做收尾。
-
重活上 isolate(compute/Isolate),不要卡主线程。
-
避免在 dispose 后 setState,检查 mounted/context.mounted。
-
需要超时就 .timeout;需要降级就 onTimeout 返回兜底。
如果你有具体场景(比如“列表页下拉刷新 + 并发请求 + 超时兜底”或“串行步骤向导、每步依赖上一步结果”),我可以按场景给一份更贴近业务的代码模板。
更多推荐
所有评论(0)