This is not yet an ECMAScript Proposal.
Before:
function fetchJson(url) {
let response = await fetch(url)
let json = await response.json()
return json.payload
}
After:
function fetchJson(url) {
return fetch(url).!await.json().!await.payload
}
Note: The
.!await
syntax is a placeholder
Right now async-await in JavaScript (and indeed in most languages that support it as a feature) requires you to do one of the following:
- Create (often unmeaningful) intermediate variables:
let intermediate1 = await doSomething()
let intermediate2 = await intermediate1.doSomethingElse()
let final = await intermediate2.andAnotherThing()
- Wrap
await
expressions with parenthesis with theawait
far from the thing its logically applies to.
let final = await (await (await doSomething()).doSomethingElse()).andAnotherThing()
Compare this to Rust syntax:
let final = doSomething().await.doSomethingElse().await.andAnotherThing().await
It's not particularly any less verbose, but it positions the await
syntax closer to where it logically applies to.
The .await
syntax alone doesn't work in JavaScript due to the fact that await
is already a valid static name in a member expression. So as a syntactic placeholder, we can use .!await
for now.
doSomething().!await
The additional benefit is that when you write an expression that returns a promise, your cursor is already positioned to type .await
.
doSomething()
// ^ cursor
doSomething().!await
// ^^^^^^^ just continue typing .!await
Rust:
let result = task.await;
Kotlin:
val result = task.await()
The Trailing Await operator is spelled .!await
as a single token with no whitespace.
Expression = Expression | TrailingAwait .
TrailingAwait = Expression ".!await" .
Trailing await should strictly be syntactic sugar for wrapping the same left-hand expression with (await <lhs>)
:
// These should function the same:
let a = expr.!await
let b = (await expr)
Will eventually bikeshed, but not important at this time.
.await
won't work because its valid JavaScript already.await!
won't work because its valid TypeScript already
Note: Please don't bully me for the pipeline proposal syntax, I'm just using the current syntax of that proposal here:
A number of different syntaxes have already been proposed in pipelines, for example:
expr |> await |> %
The final syntax of that proposal is likely to be something like:
expr |> await % |> %
But in comparison .!await
is far more convenient to write:
let a = await fetch(url) |> await %.json() |> %.payload
let b = fetch(url).!await.json().await!.payload
In fact, both syntaxes actually compliment each-other:
// Neither: Requires your cursor to jump back and forth a lot, wrapping expressions and placing `await` in the right position:
let a = baz(await bar(await foo(await doSomething()).doSomethingElse()).andAnotherThing())
// Pipelines: Doesn't require you to move your cursor to append an expression, but does to place `await`:
let b = await doSomething() |> foo(%) |> await %.doSomethingElse() |> bar(%) |> await %.andAnotherThing() |> baz(%)
// Both: You can type in a continuous stream without moving your cursor back and forth:
let c = doSomething().!await |> foo(%) |> %.doSomethingElse().!await |> bar(%) |> %.andAnotherThing().!await |> baz(%)
// Pipelines (using `|> await %`): Same, but with a lot more typing:
let d = doSomething() |> await % |> foo(%) |> %.doSomethingElse() |> await % |> bar(%) |> %.andAnotherThing() |> await % |> baz(%)