├── .gitignore ├── package.json ├── README.md ├── spec.emu └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "npm run build-loose -- --strict", 5 | "build-loose": "ecmarkup --verbose spec.emu index.html" 6 | }, 7 | "devDependencies": { 8 | "ecmarkup": "^5.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECMAScript proposal: `async do` expressions 2 | 3 | `async do` expressions allow you to introduce an asynchronous context within synchronous code without needing an immediately-invoked async function expression. 4 | 5 | This proposal builds off of the [`do` expressions proposal](https://github.com/tc39/proposal-do-expressions). 6 | 7 | This proposal has [preliminary spec text](https://tc39.github.io/proposal-async-do-expressions/). 8 | 9 | ## Motivation 10 | 11 | Currently the boundary between synchronous and asynchronous code requires defining and invoking an `async` function. In the case that you just want to perform a single operation, that's a lot of syntax for a relatively primitive operation: `(async () => {...})()`. This lets you write `async do {...}` instead. 12 | 13 | ## Examples 14 | 15 | ```js 16 | // at the top level of a script 17 | 18 | async do { 19 | await readFile('in.txt'); 20 | let query = await ask('???'); 21 | // etc 22 | } 23 | ``` 24 | 25 | ```js 26 | Promise.all([ 27 | async do { 28 | let result = await fetch('thing A'); 29 | await result.json(); 30 | }, 31 | async do { 32 | let result = await fetch('thing B'); 33 | await result.json(); 34 | }, 35 | ]).then(([a, b]) => console.log([a, b])); 36 | ``` 37 | -------------------------------------------------------------------------------- /spec.emu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
 5 | title: Async do expressions
 6 | stage: 0
 7 | contributors: Kevin Gibbons
 8 | 
9 | 10 | 11 |

Introduction

12 |

`async do` expressions allow you to introduce an asynchronous context within synchronous code without needing an immediately-invoked async function expression.

13 |
14 | 15 | 16 | 17 |

Async Do Expressions

18 |

Syntax

19 | 20 | AsyncDoExpression : 21 | `async` [no LineTerminator here] `do` Block[~Yield, +Await, ~Return] 22 | 23 |

Unlike `do` expressions, `yield` can never be used within an `async do` expression.

24 | 25 | 26 |

Static Semantics: Early Errors

27 | AsyncDoExpression: `async` `do` Block 28 |

TODO: pretty much the same list as for regular do expressions.

29 |
30 | 31 | 32 |

Runtime Semantics: Evaluation

33 | AsyncDoExpression: `async` `do` Block 34 | 35 | 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). 36 | 1. Perform ! AsyncDoStart(_promiseCapability_, |Block|). 37 | 1. Return Completion { [[Type]]: ~return~, [[Value]]: _promiseCapability_.[[Promise]], [[Target]]: ~empty~ }. 38 | 39 | 40 |

AsyncDoStart ( _promiseCapability_, _block_ )

41 |

The abstract operation AsyncDoStart takes arguments _promiseCapability_ (a PromiseCapability Record) and _block_ (a Parse Node for a |Block|). It performs the following steps when called:

42 | 43 | 1. NOTE: the algorithm below is identical to AsyncFunctionStart(_promiseCapability_, |Block|) except for its handling of the final completion value. 44 | 1. Let _runningContext_ be the running execution context. 45 | 1. Let _asyncContext_ be a copy of _runningContext_. 46 | 1. NOTE: Copying the execution state is required for the step below to resume its execution. It is ill-defined to resume a currently executing context. 47 | 1. Set the code evaluation state of _asyncContext_ such that when evaluation is resumed for that execution context the following steps will be performed: 48 | 1. Let _result_ be the result of evaluating _block_. 49 | 1. Assert: If we return here, the block either threw an exception or reached the end of its body; all awaiting is done. 50 | 1. Remove _asyncContext_ from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. 51 | 1. If _result_.[[Type]] is ~normal~, then 52 | 1. Let _result_ be UpdateEmpty(_result_, *undefined*). 53 | 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _result_.[[Value]] »). 54 | 1. Else, 55 | 1. Assert: _result_.[[Type]] is ~throw~. 56 | 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _result_.[[Value]] »). 57 | 1. [id="step-async-do-evaluation-return-undefined"] Return. 58 | 1. Push _asyncContext_ onto the execution context stack; _asyncContext_ is now the running execution context. 59 | 1. Resume the suspended evaluation of _asyncContext_. Let _result_ be the value returned by the resumed computation. 60 | 1. Assert: When we return here, _asyncContext_ has already been removed from the execution context stack and _runningContext_ is the currently running execution context. 61 | 1. Assert: _result_ is a normal completion with a value of *undefined*. The possible sources of completion values are Await or, if the async `do` doesn't await anything, step above. 62 | 1. Return. 63 | 64 |
65 |
66 |
67 | 68 | 69 |

Integration

70 |

Syntax

71 | 72 | PrimaryExpression[Yield, Await] : 73 | AsyncDoExpression 74 | 75 |

Unlike `do` expressions, `async do` expressions can be used in statement position.

76 | 77 |
78 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Async do expressions

Stage 0 Draft / January 20, 2021

Async do expressions

2058 | 2059 | 2060 |

Introduction

2061 |

async do expressions allow you to introduce an asynchronous context within synchronous code without needing an immediately-invoked async function expression.

2062 |
2063 | 2064 | 2065 | 2066 |

1 Async Do Expressions

2067 |

Syntax

2068 | 2069 | AsyncDoExpression : 2070 | async 2071 | [no LineTerminator here] 2072 | do 2073 | Block[~Yield, +Await, ~Return] 2074 | 2075 | 2076 | 2077 | Note

Unlike do expressions, yield can never be used within an async do expression.

2078 | 2079 | 2080 |

1.1 Static Semantics: Early Errors

2081 | 2082 | AsyncDoExpression : 2083 | async 2084 | do 2085 | Block 2086 | 2087 | 2088 | 2089 |

TODO: pretty much the same list as for regular do expressions.

2090 |
2091 | 2092 | 2093 |

1.2 Runtime Semantics: Evaluation

2094 | 2095 | AsyncDoExpression : 2096 | async 2097 | do 2098 | Block 2099 | 2100 | 2101 | 2102 |
  1. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  2. Perform ! AsyncDoStart(promiseCapability, Block).
  3. Return Completion { [[Type]]: return, [[Value]]: promiseCapability.[[Promise]], [[Target]]: empty }.
2103 | 2104 |

1.2.1 AsyncDoStart ( promiseCapability, block )

2105 |

The abstract operation AsyncDoStart takes arguments promiseCapability (a PromiseCapability Record) and block (a Parse Node for a Block). It performs the following steps when called:

2106 |
  1. NOTE: the algorithm below is identical to AsyncFunctionStart(promiseCapability, Block) except for its handling of the final completion value.
  2. Let runningContext be the running execution context.
  3. Let asyncContext be a copy of runningContext.
  4. NOTE: Copying the execution state is required for the step below to resume its execution. It is ill-defined to resume a currently executing context.
  5. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed:
    1. Let result be the result of evaluating block.
    2. Assert: If we return here, the block either threw an exception or reached the end of its body; all awaiting is done.
    3. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
    4. If result.[[Type]] is normal, then
      1. Let result be UpdateEmpty(result, undefined).
      2. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »).
    5. Else,
      1. Assert: result.[[Type]] is throw.
      2. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).
    6. Return.
  6. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
  7. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation.
  8. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context.
  9. Assert: result is a normal completion with a value of undefined. The possible sources of completion values are Await or, if the async do doesn't await anything, step 5.f above.
  10. Return.
2107 |
2108 |
2109 |
2110 | 2111 | 2112 |

2 Integration

2113 |

Syntax

2114 | 2115 | PrimaryExpression[Yield, Await] : AsyncDoExpression 2116 | 2117 | 2118 | 2119 | Note

Unlike do expressions, async do expressions can be used in statement position.

2120 | 2121 |
2122 |

A Copyright & Software License

2123 | 2124 |

Copyright Notice

2125 |

© 2021 Kevin Gibbons

2126 | 2127 |

Software License

2128 |

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

2129 | 2130 |

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

2131 | 2132 |
    2133 |
  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. 2134 |
  3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  4. 2135 |
  5. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.
  6. 2136 |
2137 | 2138 |

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2139 | 2140 |
2141 |
--------------------------------------------------------------------------------