Prefix `await` is cumbersome to work with.
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 solution (proposal!) is be to allow .await as a suffix operator instead of only await as a prefix operator:
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.
It's a special syntactic form, not a named member access. An await is a special kind of selector.
You can do expr?.await, expr..await and expr?..await too. It will await (of not null), then throw away the result if it's a cascade (or follow it with more selectors, expr..await.selectors, and still evaluate to the original future ... but why?)
Since this is new syntax, I'd require the operand to have a type which implies a future type (implement Future, be FutureOr, or nullable either of those, or dynamic for being dynamic). No awaiting a non-future. (You can always upcast your int to FutureOr<int> and await that, though. At least you have to be explicit about it.)
See also #1216, #2762, https://github.com/dart-lang/sdk/issues/25986, https://github.com/dart-lang/sdk/issues/23000.
(Edit, 5+ years later: Definitely with a . in front, not foo() await. Grammar is precious, .await is simple, updated to suggest only that.)
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.
Is this being considered? Are there any developments on the topic?
@lrhn
and I really like the option with dot await .await
any updates ?
whats the issue with .await syntax ?
thanks
any updates?
needed async in my WebViewController creation and I needed to get rid of the cascade :(
No updates. We've been focused on larger higher priority issues (metaprogramming, primary constructors, static member shorthands, etc.).
Maybe this extension method can solve the problem for cascade? (Not sure)
extension <T> on Future<T> {
Future<T> get wait async => await this;
}
Example:
class A {
int x = 0;
Future<int> myMethod() async => 42;
}
void main() async {
var a=A()
..myMethod().wait
..x=1;
print(a.x);
}
My guess is that it doesn't really work as intended, but creates so good an illusion that it can pass for a real thing :-) It also shows that if await suffix gets introduced, it should come with .unawaited variant to distinguish between these cases in cascade.
My guess is that it doesn't really work as intended
That's correct. The extension method here doesn't actually do anything. The caller would still need to await the result.
I would have made unawaited a getter originally, but more people preferred to see the unawaited(...) up front, where the await would have been.
If (when!!) we introduce suffix .await, I'm willing and able to introduce an
extension UnawaitedFuture on Future<Object?> {
void get unawaited {}
}
too. (Or should it be void unawaited() {} so you need to write .unawaited()? Name doesn't work that well for a function, it should be a verb, so future.unawait();. Nailed it!)
This would certainly be super nice to have.
.await as a member access is very nice for chaining - though it might look a little odd to do <Future>[].wait.await; but I guess we'd just have to get used to it.
I actually prefer unawaited as a getter since were just using it to ignore/throw away the value
unawait() implies we're doing something to the future (like .ignore()) but we're not.
I actually created that extension myself a couple times.