Dart 异步编程与生成器详解:Future、Stream 及 typedef 用法

巷口听风
巷口听风
发布时间:2026-10-15 | 5人浏览过
原创

Dart 提供了强大的异步处理能力,通过 Future 处理单次异步操作结果,利用 Stream 接收连续事件流。掌握 async/await、yiled 生成器及 typedef 类型别名,可显著提升代码的可读性与维护性。

异步支持

Dart 标准库中包含大量返回 Future 或 Stream 对象的函数。这类被称为异步函数的操作通常涉及 IO 等耗时任务,它们不会阻塞当前线程等待完成,而是在任务完成后通过回调通知程序。

Future 基础概念

Future 类似于 JavaScript 中的 Promise,代表一个异步操作的最终完成(成功)或失败。其核心特征是每个 Future 对象只对应一个结果状态。所有 Future 的 API 返回值本身仍是 Future 对象,因此支持便捷的链式调用。

Future 常用方法

在实际开发中,常使用 Future.delayed 模拟耗时操作(如网络请求)。以下是 then、catchError 和 whenComplete 的具体用法:

第1步:使用 then 获取成功结果

then 用于在异步任务成功后执行回调。以下示例模拟一个 2 秒后返回 "hi world!" 的任务:

Future.delayed(Duration(seconds: 2), () {
  return "hi world!";
}).then((data) {
  print(data);
});

第2步:使用 catchError 或 onError 捕获异常

若异步过程抛出错误,可通过 catchError 独立捕获,或在 then 中添加 onError 参数进行内联处理:

// 方式一:独立 catchError
Future.delayed(Duration(seconds: 2), () {
  throw AssertionError("Error");
}).then((data) {
  print("success");
}).catchError((e) {
  print(e);
});

// 方式二:使用 then 的 onError 参数
Future.delayed(Duration(seconds: 2), () {
  throw AssertionError("Error");
}).then((data) {
  print("success");
}, onError: (e) {
  print(e);
});

第3步:使用 whenComplete 执行收尾工作

当无论成功或失败都需要执行清理逻辑(如关闭加载弹窗)时,应使用 whenComplete。它会在前序操作结束后无差别触发:

Future.delayed(Duration(seconds: 2), () {
  throw AssertionError("Error");
}).then((data) {
  print(data);
}).catchError((e) {
  print(e);
}).whenComplete(() {
  // 无论成功或失败都会走到这里
});

第4步:使用 wait 并发处理多个任务

Future.wait 接受一个 Future 列表。只有当列表中所有 Future 都成功完成时,才会触发成功回调;只要有一个失败,即触发整体错误回调。这与 JavaScript 的 Promise.all() 逻辑一致。

以下示例模拟两个不同耗时的异步任务,拼接结果:

Future.wait([
  Future.delayed(Duration(seconds: 2), () {
    return "hello";
  }),
  Future.delayed(Duration(seconds: 4), () {
    return " world";
  })
]).then((results) {
  print(results[0] + results[1]);
}).catchError((e) {
  print(e);
});
// 注:因第二个任务耗时 4 秒,总耗时取决于最慢的一个任务

async 和 await 关键字

虽然链式调用直观,但深层嵌套会影响可读性。引入 async 和 await 可使异步代码呈现同步风格。

  • async:修饰函数,表示该函数包含异步操作,自动将函数返回值包装为 Future。
  • await:暂停执行直到后续 Future 完成并返回结果,必须位于 async 函数内部。

对比同步与异步函数的定义:

// 普通同步函数
String lookUpVersion() => '1.0.0'; 

// 异步函数(返回值强制转为 Future<String>)
Future<String> lookUpVersion() async => '1.0.0';

推荐使用 try-catch 结构包裹 await 以优雅处理异常:

task() async {
  try {
    String id = await login("alice", "******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    // 继续后续逻辑
  } catch (e) {
    // 统一错误处理
    print(e);
  }
}

处理流 (Stream)

Stream 用于处理连续的数据事件流,适用于文件读写、网络下载或 UI 事件等场景。相比仅返回单一结果的 Future,Stream 可以触发多次成功或失败事件。

使用 await for 遍历流

处理 Stream 主要采用 await for 循环。注意:await for 会阻塞直至流关闭,且要求外层作用域标记为 async。在非无限流场景下使用能有效提升代码清晰度。

语法结构与流程如下:

await for (varOrType identifier in expression) {
   // stream 每 emit 一次值,此处即执行一次
}

若要提前终止监听,可在循环体内使用 break 或 return 取消订阅。

使用 Stream API 监听数据

通过 listen 方法可以更灵活地处理流的生命周期事件:

  • data:接收到正常数据时触发。
  • onError:发生异常时触发。
  • onDone:流正常关闭时触发。

以下为结合 Future.delayed 构造多阶段流的示例:

Stream.fromFutures([
  Future.delayed(Duration(seconds: 1), () {
    return "hello 1";
  }),
  Future.delayed(Duration(seconds: 2), () {
    throw AssertionError("Error");
  }),
  Future.delayed(Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen(
  (data) {
    print(data);
  },
  onError: (e) {
    print(e.message);
  },
  onDone: () {
    // 流结束
  }
); /*
输出顺序:
hello 1
Error
hello 3
*/

生成器

生成器函数支持按需延迟生成值序列。Dart 提供两种模式:

  • 同步生成器:返回 Iterable,函数体标记为 sync*。
  • 异步生成器:返回 Stream,函数体标记为 async*。

两者均使用 yield 语句逐个产出值。对于递归生成的场景,可使用 yield* 提高性能:

// 同步生成器示例
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

// 递归优化示例
Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

类型别名 (typedef)

Dart 中函数是一等公民。typedef 可用于为特定函数签名创建类型别名,从而保留完整的类型信息,便于静态分析和重构工具识别。

未使用 typedef 时,作为字段传递的函数往往丢失具体的参数与返回类型约束,仅被识别为泛型 Function。使用 typedef 后可强化类型安全:

// 定义函数类型别名
typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;
  SortedCollection(this.compare);
}

void main() {
  int sort(Object a, Object b) => 0;
  SortedCollection coll = SortedCollection(sort);
  
  // 此时断言校验生效,因为 retain 了具体签名
  assert(coll.compare is Compare); 
}

此外,typedef 也支持泛型,例如:

typedef Compare<T> = int Function(T a, T b);
assert(sort is Compare<int>); // True!

元数据

元数据用于向编译器或运行时提供关于代码的附加说明,由 @ 开头标识。Dart 内置了两个常用元数据注解:

  • @deprecated:标记过时的 API,并在 IDE 提示用户迁移至新接口。
  • @override:显式声明该方法重写了父类成员,防止拼写错误导致的意外覆盖。
class Television {
  @deprecated
  void activate() {
    turnOn();
  }
  void turnOn() { ... }
}

开发者也可自定义元数据类,通过构造函数传参,实现类似装饰器的效果:

library todo;
class Todo {
  final String who;
  final String what;
  const Todo(this.who, this.what);
}

import 'todo.dart';

@Todo('dev', 'implement logic here')
void doSomething() {
  print('doing...');
}

元数据可应用于库、类、字段、函数等声明处,并支持在运行时通过反射机制获取。

以上就是 Dart 异步编程、生成器及自定义类型用法的详细内容,更多关于 Dart 核心库开发的资料请关注本站其它相关文章!
相关标签:

本站声明

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

Python 相关产品和应用

全部下载

专题列表