dart-lang / language

Design of the Dart language
Other
2.65k stars 200 forks source link

Parallel asynchronous context operator #2412

Open lrhn opened 2 years ago

lrhn commented 2 years ago

Inspired by #2321, this is an idea for performing "parallel" asynchronous computations in async functions.

Rather than doing parallel awaits on futures, this feature allows separate asynchronous expressions to be evaluated in parallel (interleaved) rather than sequentially. Each expression can contain one or more awaits.

Example syntax:

 await*(e1, e2, e3)

This is an expression, theawait* must be followed by a parenthesized, comma-separated (trailing comma allowed) list of expressions.

If the static type of e1 is T1, etc., the static type of await*(e1, e2, e3) is (T1, T2, T3), just as it would be without the await*.

The difference is that when e1 hits its first asynchronous supension (if any), rather than suspending the entire method body, evaluation proceeds to evaluation of e2 and then e3. The initial, synchronous, part of the computation of e1, e2 and e3 happens synchronously, then each computation can continue when its await completes, until its next await. When all (three here) expressions have computed a value, the await* expression completes with a record of those values.

If any of the expressions completes by throwing, so will the await* expression, but not until all expressions have completed. The error thrown, perhaps a ParalleleAwaitError, will contain a tuple of the same width as the expected result, with the successful values on their corresponding positions, and null in the positions of the expressions which threw, as well as a collection of the errors in some form.

You can nest await*, but you have to do it explicitly:

var result = await*(e1, e2, await*(e3, e4));

This will evaluate e1, e2, e3 and e4 in parallel.

This differs from what you can write using existing Dart, since it doesn't just expect a Future at each position. Instead it allows an asynchronous computation in the current scope. It's basically a call-by-name parallel await function where each expression acts like it computes to a future, but without actually needing to do that.

If you want to await several heterogeneously typed futures, you can do so as var (v1, v2, v3) = await*(await f1, await f2, await f3);.

eernstg commented 2 years ago

Very interesting, :tada: