2024/1/1 Hokkaido

Say Goodbye to Future.wait([]) in Dart

Andrew Chen
2 min readMay 3, 2024

As Dart developers, we’re no strangers to asynchronous programming and the power of Futures. In the past, when we needed to wait for multiple futures concurrently, we relied on the Future.wait([]) method, which returned a List<T>. However, this approach had a significant drawback: we had to manually cast the results to their desired types, which could lead to verbose and error-prone code.

final results = await Future.wait([
Future.value("andrew"),
Future.value(1984),
]);
expect((results[0] as String).toUpperCase(), "ANDREW");
expect((results[1] as int).isNegative, false);

Fortunately, there’s a more elegant solution that leverages the power of extensions and async/await syntax: the zipWith extension method.

Introducing zipWith

The zipWith extension method allows you to combine the results of two futures into a typed tuple, eliminating the need for manual casting. Here's how it works:

final (name, year) = await Future.value("andrew")
.zipWith(Future.value(1984));
expect(name.toUpperCase(), "ANDREW");
expect(year.isNegative, false);

In this example, we’re combining the results of two futures: one that resolves to the string “andrew” and another that resolves to the integer 1984. The zipWith method returns a tuple (T, T2) where T is the type of the first future, and T2 is the type of the second future.

But wait, there’s more! You can even chain multiple zipWith calls to combine the results of three or more futures:

final ((name, year), married) = await Future.value("andrew")
.zipWith(Future.value(1984))
.zipWith(Future.value(false));
// ...
expect(married, false);

In this example, we’re combining three futures, resulting in a nested tuple ((T, T2), T3).

Under the Hood

So, how does zipWith work its magic? Here's the implementation:

extension FutureZipX<T> on Future<T> {
Future<(T, T2)> zipWith<T2>(Future<T2> future2) async {
late T result1;
late T2 result2;
await Future.wait([
then((it) => result1 = it),
future2.then((it) => result2 = it)
]);
return (result1, result2);
}
}

Wrapping Up

By embracing the power of zipWith, you can write cleaner, more expressive code when working with multiple asynchronous operations. No more manual casting or fiddling with List<dynamic> – just concise, type-safe tuples that make your code easier to read and maintain.

So, the next time you find yourself juggling multiple futures, give zipWith a try and experience the joy of streamlined asynchronous programming in Dart.

See Also

DardPad:

If you do have many properties need to combine from futures for example with a bad restful APIs design:

Another way:

final a = Future.value("a");
final b = Future.value("b");
final c = Future.value("c");
final d = Future.value(4);
UserProfile(await a, await b, await c, await d);

Flatten nested tuples: https://gist.github.com/yongjhih/89a199ec14127f92a001d2e15e8a32ac

--

--