Dart 3 Record:解决 Future.wait 异构返回值的类型问题

17 阅读2分钟

在 Flutter 开发中,经常会遇到多个接口需要同时请求的场景。

例如进入图书详情页时,需要同时获取书籍信息和收藏状态:

final infoFuture = bookApi.getBookInfo(bookId);
final favoriteFuture = favoriteApi.isFavorite(bookId);

两个请求互不依赖,可以使用 Future.wait:

final responses = await Future.wait([
  infoFuture,
  favoriteFuture,
]);

这种写法可以并发等待多个 Future,但当这些 Future 的返回类型不同时,会出现一个问题:结果列表无法保留每个元素原本的具体类型。

Future.wait 的类型问题

假设两个请求的返回类型分别是:

Future<BookInfo> infoFuture;
Future<bool> favoriteFuture;

我们使用:

final responses = await Future.wait([
  infoFuture,
  favoriteFuture,
]);

两个 Future 返回的类型不同:

BookInfo
bool

但 Future.wait 最终返回的是一个 List。

这时无法得到类似:

List<BookInfo, bool>

这样的类型。

因此,实际使用时通常需要进行类型转换:

final bookInfo = responses[0] as BookInfo;
final isFavorite = responses[1] as bool;

完整代码:

final responses = await Future.wait([
  infoFuture,
  favoriteFuture,
]);

final bookInfo = responses[0] as BookInfo;
final isFavorite = responses[1] as bool;

代码能运行,但有两个问题。

第一,需要自己记住 responses[0] 和 responses[1] 分别是什么。

第二,需要手动使用 as 转换类型。

当 Future 增多后:

final results = await Future.wait([
  bookFuture,
  favoriteFuture,
  commentsFuture,
  authorFuture,
]);

后面就会出现:

final book = results[0] as Book;
final favorite = results[1] as bool;
final comments = results[2] as List<Comment>;
final author = results[3] as Author;

这不仅不直观,也失去了 Dart 静态类型系统带来的便利。

Dart 3 使用 Record 保留类型

如果项目使用 Dart 3,可以使用 Record。

把原来的:

final responses = await Future.wait([
  infoFuture,
  favoriteFuture,
]);

改成:

final (bookInfo, isFavorite) = await (
  infoFuture,
  favoriteFuture,
).wait;

这里的两个 Future 仍然会并发执行。

区别在于,Record 可以保留每个位置对应的具体类型。

也就是说:

final (bookInfo, isFavorite) = await (
  infoFuture,
  favoriteFuture,
).wait;

得到的变量类型分别是:

bookInfo   → BookInfo
isFavorite → bool

不需要再写:

as BookInfo
as bool

也不需要通过:

results[0]
results[1]

访问结果。