Dart Future 讲清楚:定义、创建方式、组合处理与结果回调

唐可
唐可
发布时间:2026-08-25 | 14人浏览过
原创

前言

刚接触 Dart 异步编程时,Future 往往最先出现,但很多人知道它和“未来”有关,却说不清它到底保存了什么、什么时候完成、该怎样接住结果。本文按“是什么、怎么创建、多个任务怎么协作、结果怎么处理”的顺序重写这套知识点,保留关键 API、示例代码和行为差异,方便你在读完后直接判断某个异步场景该用哪种 Future 写法。

刚接触 Dart 异步编程时,Future 往往最先出现,但很多人知道它和“未来”有关,却说不清它到底保存了什么、什么时候完成、该怎样接住结果。本文按“是什么、怎么创建、多个任务怎么协作、结果怎么处理”的顺序重写这套知识点,保留关键 API、示例代码和行为差异,方便你在读完后直接判断某个异步场景该用哪种 Future 写法。

Future 到底表示什么

在 Dart 中,Future 表示一个可能还没有完成的异步操作结果。重点不在“操作”本身,而在“结果现在拿不到,但将来会有”。

如果把它放到日常场景里理解,可以把一次异步请求看成“点外卖”:

  • 主线程发起请求,就像你下单。
  • 等待配送的过程不会阻塞你手头的事,你可以继续执行后续代码。
  • 等骑手送达,相当于异步操作完成,结果再被通知回来。

这也是 Dart 单线程异步模型的关键:耗时 I/O 并不是卡住当前线程硬等,而是借助事件循环机制,把异步 I/O 交给底层,等系统回调后再把结果交回程序。因此,Future 本质上就是对“未来某个时间点才会拿到的值或错误”的封装。

Future 有哪些创建方式

创建 Future,本质上就是把一段同步或异步逻辑包装成“稍后可得到结果”的对象。文中给出了六种工厂构造方式,再加上 async/await 语法糖,基本覆盖了日常使用场景。

展示 Dart Future 创建方式与适用场景的对照信息图
Future 创建方式速查把常见 Future 创建方式放在一张图里。

Future()

Future(FutureOr<T> computation()) 需要传入一个返回类型为 FutureOr<T> 的函数。这里的 FutureOr<T> 表示:返回值既可以是同步值,也可以是一个 Future。

它的执行逻辑可以概括为几步:先创建一个未完成的 Future,再把传入的 computation 放进事件队列,执行后把结果或错误写回这个 Future,最后再把它返回给调用方。

factory Future(FutureOr<T> computation()) {
  // 1、创建一个未完成的_Future 实例 result。
  _Future<T> result = new _Future<T>();
  // 2、调度异步执行
  Timer.run(() { // 将computation加入事件队列。
    FutureOr<T> computationResult;  // 声明FutureOr<T>类型的变量。
    try {
      // 3、执行传入的异步操作的代码
      computationResult = computation();
    } catch (e, s) {
      // 4、执行过程中间出现错误的处理。将错误信息传递给_Future实例
      _completeWithErrorCallback(result, e, s);
      return;
    }
    // 5、结果处理。
    result._complete(computationResult);
  });
  // 6、返回异步操作执行的结果
  return result;
}

下面这个例子返回的是一个同步值,但依旧能被 Future 包装起来:

FutureOr<int> buildComputation() {
  int age = 10;
  return age;
}
void main() {
    Future(buildComputation);
}

Future.delayed()

这个构造方式用来创建“延迟执行”的异步任务,必须指定延迟时间。

参数为 Duration duration, [FutureOr<T> computation()?],其中 computation 可以省略。

 Future.delayed(Duration(seconds: 20),()=>4);

它适合定时触发、模拟网络等待,或者明确需要把任务放到未来某个时刻再执行的场景。

Future.value() 与 Future.error()

这两个 API 都是“立刻创建已完成状态”的 Future,只是一个代表成功,一个代表失败。

Future.value() 立即返回一个携带成功值的 Future:

Future.value(['Dart',234]);

Future.error() 立即返回一个失败状态的 Future,并可附带堆栈信息:

Future.error(Exception('未满18岁!'),StackTrace.current);

如果你已经知道结果是什么,或者明确要把错误包装成异步结果抛出,这两种写法会比手动再包一层 Future() 更直接。

Future.microtask() 与 Future.sync()

Future.microtask() 会把任务加入微任务队列。它和普通 Future() 的差别不在“是不是异步”,而在于调度优先级更高,通常会先于事件队列中的任务执行。

void main() {
    print('同步任务A');
    Future(()=>print('事件任务'));
    Future.microtask(()=>print('微任务'));
    print('同步任务B');
}
输出:
同步任务A
同步任务B
微任务
事件任务

这个输出顺序正好说明了三层关系:同步代码先执行,随后清空微任务队列,最后才轮到事件队列。

Future.sync() 的作用则是把同步代码和异步代码统一包装成 Future,方便外部用一致的方式接收结果。

Future.sync(
    (){
      print('同步代码');
      Future(()=>1);
    });

当一段逻辑内部有时同步返回、有时异步返回时,Future.sync() 很适合用来统一出口。

async/await:最常用的语法糖

相比直接链式调用,async/await 更接近同步代码的书写方式,因此也是 Dart 中最常见的 Future 使用形态。

规则很简单:

  • 函数用 async 标记后,会返回 Future。
  • await 用来等待一个异步操作完成。
  • await 只能出现在 async 函数内部。
Future<void> dealTask() async {
  await Future.delayed(Duration(seconds: 1)); 
}

如果你主要关注业务流程可读性,通常优先考虑 async/await;如果更需要链式组合、细粒度回调或统一调度,再回到 Future 的工厂方法和实例方法。

多个 Future 一起处理时怎么选

单个 Future 解决的是“一个异步结果什么时候到”,多个 Future 则要回答“多个异步任务之间是什么关系”。文中给出的四个静态方法,对应四类不同需求。

展示多个 Future 组合方式与结果处理链的关系信息图
多个 Future 的组合与收尾多个 Future 的组合方式和结果处理方法经常一起出现。

Future.any()

Future.any() 会从多个 Future 中取最先完成的那个,不要求其他任务也都结束。

Future.any([
  Future.delayed(Duration(seconds: 100),() => print('A')),
  Future.delayed(Duration(seconds: 80),() => print('B')),
  Future.delayed(Duration(seconds: 40),() => print('C'))
]);

这种写法适合“谁先返回就用谁”的场景,例如多源请求、超时备选服务等。

Future.forEach() 与 Future.doWhile()

Future.forEach() 面向集合遍历,强调“逐个异步处理元素”。

Future.forEach([1,2,3,4,5], (value) => print(value));

Future.doWhile() 面向重复执行,直到返回 false 才终止,更像异步版本的循环控制。

Future.doWhile(()async{
  for(int i in [1,9,3,4,5,6,7,8]){
    if(i%2==0){
      return false;
    }
    print('i:$i');
  };
  return true;
});

如果任务核心是“遍历一组数据”,优先看 forEach;如果核心是“反复执行直到条件变化”,更适合用 doWhile。

Future.wait()

Future.wait() 用来并发执行多个 Future,并在全部完成后返回结果列表。它是批量异步任务里最常用的组合方式。

参数如下:

  • Iterable<Future<T>> futures
  • {bool eagerError = false, void cleanUp(T successValue)?}

其中:

  • eagerError 为 true 时,遇到第一个错误就提前终止。
  • cleanUp 用于任务取消或失败后的清理回调。
Future.wait([
  Future((){
    print('A');
    throw Exception('A异常');
  }),
  Future((){
    print('B');
    // throw Exception('B异常');
  }),
  Future(()=>print('C'))
],eagerError: false,);

它适合“所有结果都要收齐再继续”的业务,比如同时加载多个接口、并发初始化多个模块、批量上传后统一汇总状态等。

Future 的结果和错误如何处理

Future 最终只有两类结果:成功,或者失败。围绕这两类状态,Dart 提供了一组链式处理方法。

  • then():处理成功结果。
  • catchError():处理捕获到的错误。
  • whenComplete():无论成功还是失败都会执行,常用于清理资源。
  • timeout():给异步操作增加超时熔断。
  • onError():更偏类型安全的错误处理方式。
  • asStream():把 Future 转成单元素 Stream。

下面这段代码把常见结果处理方式串到了一起:

Future.delayed(Duration(microseconds: 5000), (){print('异步代码');
    throw Exception('错误');})
    .then((result) => print('未携带返回结果'))
    .catchError((error) => print('$error'))
    .whenComplete(() => print('清理资源'))
    .timeout(Duration(seconds: 20))
    .onError((e,s){print('$e');});

实际使用时可以这样理解:

  • 只关心成功值,用 then()。
  • 需要兜底异常,接上 catchError()。
  • 有关闭连接、释放资源、结束 loading 之类的收尾逻辑,用 whenComplete()。
  • 外部依赖不可靠时,加上 timeout() 防止无限等待。

如果项目风格偏向同步式书写,很多时候你会直接用 try/await/catch 来替代部分链式调用;但理解这些底层实例方法依然重要,因为不少框架代码和底层封装仍然以 Future 链为主。

一文梳理:Future 应该怎么记

把整篇内容压缩后,Future 可以从三条主线来记:

  • 第一条是“它是什么”:Future 表示未来才能得到的异步结果。
  • 第二条是“怎么创建”:常见方式包括 Future()、Future.delayed()、Future.value()、Future.error()、Future.microtask()、Future.sync() 以及 async/await。
  • 第三条是“怎么协作和收尾”:多个任务可以用 Future.any()、Future.forEach()、Future.doWhile()、Future.wait() 组合,结果则通过 then()、catchError()、whenComplete()、timeout()、onError()、asStream() 处理。

如果你要快速做选择,可以按这个顺序判断:

  • 要把一段逻辑包装成异步结果,先选创建方式。
  • 有多个任务并行或竞争,再选组合方式。
  • 最后根据成功、失败、超时和清理需求,补上对应的结果处理链。

这样理解后,Future 就不再只是一个抽象名词,而是一套完整的 Dart 异步编程组织方式。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表