In an asynchronous Dart function, the await expr expression allows blocking and waiting for a future result of expr to complete.
This is highly convenient compared to using Future.then, but grammatically it's still cumbersome because await is a prefix operator with lower precedence than selectors.
Example:
var x = await (await foo.bar()).baz();
If you need to await an intermediate result of a longer computation chain, you need to add parentheses and go back and write the await far distanced from the operation that created the future.
If you have a cascade like:
expr
..bar()
..baz()
..qux();
and baz is (or becomes) asynchronous and you want to await it before continuing, then you have to rewrite it to something like:
var tmp = expr..bar();
await tmp.baz();
tmp..qux(); // or one dot, doesn't matter.
A possible improvement would be to allow await as a suffix operator instead of a prefix operator:
var x = foo.bar() await.baz() await;
expr
..bar()
..baz() await
..qux();
or maybe with a dot in front:
var x = foo.bar().await.baz().await;
expr
..bar()
..baz().await
..qux();
This is still only allowed inside asynchronous functions where await is a reserved word, so it would not be ambiguous.
Other alternatives could be a suffix operator (like !, although that's probably high-priced syntactic real-estate, but if it is a general heuristic coercion operator, it could coerce from Future<T> to T too) or something else that allows binding await better in compound expressions.
Idea: Special-case the await keyword to be a valid RHS for the |> operator from #43. It could look like this:
/// Changes the contents of [file] to uppercase.
/// Completes when the file is written back to disk.
Future<void> contentsToUpperCase(File file) async =>
file.readAsString()
|> await
|. toUpperCase()
|> file.writeAsString
|> await;
Is this still being considered? It looks like this would be a very small non-breaking syntactic change, but would make working with nested futures _much_ more pleasant. For example, I'm currently working with records that form a graph in a database so that each record has a children getter of type Future<List<Record>> (these graphs can be cyclic, so I do not want to immediately load the children and their descendants). Right now this forces me to write call chains like the following:
final someChild = (await (await (await db.get(someId)).children).first.children).first;
With the proposed postfix await this would become much less cumbersome:
final someChild = db.get(someId).await.children.await.first.children.await.first;
(Of course something even shorter than .await would be great, but now that ! is already used for NNBD I would personally already be very happy with .await or any postfix solution really.)
@fkettelhoit
In these cases I will simply use .then, as function chaining is way clearer than await chaining...
final someChild =
await db.get(someId)
.then((o) => o.children)
.then((o) => o.first.children)
.then((o) => o.first)
This would be even better if we could have something like #691.
To be honest I think
expr
..bar()
..baz() await
..qux();
and
expr
..bar()
..baz().await
..qux();
are kinda confusing because I'm just so used to seeing await infront of the Future.
I think the ..baz().await at least shows that the await does indeed wait for baz, in the other one I thought await would wait for qux() at first.
Maybe I'm just boring, but what about this?
expr
..bar()
..await baz()
..qux();
The only thing i don't like about it is that baz is not directly under bar but I think it's still good enough to quickly scan over.
Putting the await before the method call is something I have otherwise thought of, also for other prefix operators.
The await foo().bar() issue is also a problem for !foo.bar(). If you could put prefix operators inside a selector sequence, then you could write that as foo().!bar() (which is perhaps more readable as list.!isEmpty).
It doesn't really work as an incremental change, though. To do this, it would mean that foo.!bar.baz should bind to the bar, not the bar.baz, but then it's inconsistent that !foo.bar.baz does not bind to the foo. We'd have to change the meaning of existing code, making it a breaking language change. For example foo.- is a proposed syntax for doing an operator tear-off. so foo.-[x] would be ambiguous.
So, not sure that feature is so great it's worth a breaking syntax change.
For reference, Rust decided to use postfix syntax for await. I like prefix await for most cases, but it would be nice to support a postfix form in method chains too.
@JSANL wrote:
Maybe I'm just boring, but what about this?
expr ..bar() ..await baz() ..qux();
Totally in favor of this syntax!
Using a prefix operator inline in a selector chain is an enticing idea.
It generalizes, so we could allow foo.!isEmpty or bar.-value as well. The issue with that is that it might impose on our wish to do bar.- as the minus operator tear-off.
Just wanted to point out that the current proposal treats "await" as if it were a noun whereas, in reality, it's a verb. If we agree on it being a verb, which is a reasonable characterization, then we have to consider adding parentheses: .await(). Without parentheses, .await is a tear-off.
Most helpful comment
For reference, Rust decided to use postfix syntax for
await. I like prefixawaitfor most cases, but it would be nice to support a postfix form in method chains too.