Skip to content

Instantly share code, notes, and snippets.

@jamiebuilds
Last active June 29, 2022 02:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamiebuilds/c242c96222366125f35e058d813afac8 to your computer and use it in GitHub Desktop.
Save jamiebuilds/c242c96222366125f35e058d813afac8 to your computer and use it in GitHub Desktop.

Trailing .!await

Status

This is not yet an ECMAScript Proposal.

Authors

Overview

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

Motivation

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:

  1. Create (often unmeaningful) intermediate variables:
let intermediate1 = await doSomething()
let intermediate2 = await intermediate1.doSomethingElse()
let final = await intermediate2.andAnotherThing()
  1. Wrap await expressions with parenthesis with the await 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

Prior Art

Rust:

let result = task.await;

Kotlin:

val result = task.await()

Syntax

The Trailing Await operator is spelled .!await as a single token with no whitespace.

Expression    = Expression | TrailingAwait .
TrailingAwait = Expression ".!await" .

Semantics

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)

Open Issues

What should the final syntax be?

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

FAQ

Why not build this feature into the |> pipelines proposal?

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(%)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment