From a2647114b7f2d42b02e7c04c8c3a05787846f6e5 Mon Sep 17 00:00:00 2001 From: Maya Lekova Date: Fri, 8 Jun 2018 09:00:53 +0200 Subject: [PATCH 1/3] Normative: Reduce the number of ticks in async/await (#1250) JavaScript programmers may expect that the following two programs are largely similar in terms of how they perform with respect to the ECMAScript job queue (if inside of an async function): promise.then(f); f(await promise); However, if `promise` is a built-in promise, then these two code fragments will differ in the number of iterations through the job queue are taken: because `await` always wraps a Promise with another Promise, there are three job queue items enqueued and dequeued before calling `f` in the `await` example, whereas there is just a single item for the `then` usage. In discussions with JavaScript programmers, the number of job queue items in the current semantics turns out to be surprising. For example, the difference has become more visible in conjunction with new V8 features for Promise visibility and debugging, which are sometimes used in Node.js. This patch changes the semantics of await to reduce the number of job queue turns that are taken in the common `await` Promise case by replacing the unconditional wrapping with a call to PromiseResolve. Analogous changes are made in async iterators. The patch preserves key design goals of async/await: - Job queue processing remains deterministic, including both ordering and the number of jobs enqueued (which is observable by interspersing other jobs) - Userland Promise libraries with "then" methods ("foreign thenables") are usable within `await`, and trigger a turn of the job queue - Non-Promises can be awaited, and this takes a turn of the native job queue (as do all usages of `await`) Reducing the number of job queue turns also improves performance on multiple highly optimized async/await implementations. In a draft implementation of this proposal in V8 behind a flag [1]: - The doxbee async/await performance benchmark [2] improved with 48% - The fibonacci async/await performance benchmark [3] improved with 23% - The Hapi throughput benchmark [4] improved with 50% (when async hooks are enabled) and with 20% (when async hooks are disabled) [1] https://chromium-review.googlesource.com/c/v8/v8/+/1106977 [2] https://github.com/bmeurer/promise-performance-tests/blob/master/lib/doxbee-async.js [3] https://github.com/bmeurer/promise-performance-tests/blob/master/lib/fibonacci-async.js [4] https://github.com/fastify/benchmarks --- spec.html | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/spec.html b/spec.html index 09c62ca6cf..d6f7046b6c 100644 --- a/spec.html +++ b/spec.html @@ -2990,22 +2990,21 @@

Await

Algorithm steps that say

- 1. Let _completion_ be Await(_promise_). + 1. Let _completion_ be Await(_value_).

mean the same thing as:

1. Let _asyncContext_ be the running execution context. - 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). - 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _promise_ »). + 1. Let _promise_ be ? PromiseResolve(« _value_ »). 1. Let _stepsFulfilled_ be the algorithm steps defined in . 1. Let _onFulfilled_ be CreateBuiltinFunction(_stepsFulfilled_, « [[AsyncContext]] »). 1. Set _onFulfilled_.[[AsyncContext]] to _asyncContext_. 1. Let _stepsRejected_ be the algorithm steps defined in . 1. Let _onRejected_ be CreateBuiltinFunction(_stepsRejected_, « [[AsyncContext]] »). 1. Set _onRejected_.[[AsyncContext]] to _asyncContext_. - 1. Perform ! PerformPromiseThen(_promiseCapability_.[[Promise]], _onFulfilled_, _onRejected_). + 1. Perform ! PerformPromiseThen(_promise_, _onFulfilled_, _onRejected_). 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. 1. Set the code evaluation state of _asyncContext_ such that when evaluation is resumed with a Completion _completion_, the following steps of the algorithm that invoked Await will be performed, with _completion_ available. 1. Return. @@ -3018,14 +3017,14 @@

Await

Await can be combined with the `?` and `!` prefixes, so that for example

- 1. Let _value_ be ? Await(_promise_). + 1. Let _result_ be ? Await(_value_).

means the same thing as:

- 1. Let _value_ be Await(_promise_). - 1. ReturnIfAbrupt(_value_). + 1. Let _result_ be Await(_value_). + 1. ReturnIfAbrupt(_result_). @@ -36909,12 +36908,11 @@

AsyncFromSyncIteratorContinuation ( _result_, _promiseCapability_ )

1. IfAbruptRejectPromise(_done_, _promiseCapability_). 1. Let _value_ be IteratorValue(_result_). 1. IfAbruptRejectPromise(_value_, _promiseCapability_). - 1. Let _valueWrapperCapability_ be ! NewPromiseCapability(%Promise%). - 1. Perform ! Call(_valueWrapperCapability_.[[Resolve]], *undefined*, « _value_ »). + 1. Let _valueWrapper_ be ? PromiseResolve(« _value_ »). 1. Let _steps_ be the algorithm steps defined in . 1. Let _onFulfilled_ be CreateBuiltinFunction(_steps_, « [[Done]] »). 1. Set _onFulfilled_.[[Done]] to _done_. - 1. Perform ! PerformPromiseThen(_valueWrapperCapability_.[[Promise]], _onFulfilled_, *undefined*, _promiseCapability_). + 1. Perform ! PerformPromiseThen(_valueWrapper_, _onFulfilled_, *undefined*, _promiseCapability_). 1. Return _promiseCapability_.[[Promise]].
@@ -37535,15 +37533,14 @@

AsyncGeneratorResumeNext ( _generator_ )

1. If _state_ is `"completed"`, then 1. If _completion_.[[Type]] is ~return~, then 1. Set _generator_.[[AsyncGeneratorState]] to `"awaiting-return"`. - 1. Let _promiseCapability_ be ! NewPromiseCapability(%Promise%). - 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _completion_.[[Value]] »). + 1. Let _promise_ be ? PromiseResolve(« _completion_.[[Value]] »). 1. Let _stepsFulfilled_ be the algorithm steps defined in . 1. Let _onFulfilled_ be CreateBuiltinFunction(_stepsFulfilled_, « [[Generator]] »). 1. Set _onFulfilled_.[[Generator]] to _generator_. 1. Let _stepsRejected_ be the algorithm steps defined in . 1. Let _onRejected_ be CreateBuiltinFunction(_stepsRejected_, « [[Generator]] »). 1. Set _onRejected_.[[Generator]] to _generator_. - 1. Perform ! PerformPromiseThen(_promiseCapability_.[[Promise]], _onFulfilled_, _onRejected_). + 1. Perform ! PerformPromiseThen(_promise_, _onFulfilled_, _onRejected_). 1. Return *undefined*. 1. Else, 1. Assert: _completion_.[[Type]] is ~throw~. From ec07f4edd0a04d3aefe2d03439cb4715f791b62f Mon Sep 17 00:00:00 2001 From: Maya Lekova Date: Fri, 29 Jun 2018 10:03:52 +0200 Subject: [PATCH 2/3] Editorial: Add missing parameter to PromiseResolve calls (#1250) --- spec.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec.html b/spec.html index d6f7046b6c..8ed9279886 100644 --- a/spec.html +++ b/spec.html @@ -2997,7 +2997,7 @@

Await

1. Let _asyncContext_ be the running execution context. - 1. Let _promise_ be ? PromiseResolve(« _value_ »). + 1. Let _promise_ be ? PromiseResolve(%Promise%, « _value_ »). 1. Let _stepsFulfilled_ be the algorithm steps defined in . 1. Let _onFulfilled_ be CreateBuiltinFunction(_stepsFulfilled_, « [[AsyncContext]] »). 1. Set _onFulfilled_.[[AsyncContext]] to _asyncContext_. @@ -36908,7 +36908,7 @@

AsyncFromSyncIteratorContinuation ( _result_, _promiseCapability_ )

1. IfAbruptRejectPromise(_done_, _promiseCapability_). 1. Let _value_ be IteratorValue(_result_). 1. IfAbruptRejectPromise(_value_, _promiseCapability_). - 1. Let _valueWrapper_ be ? PromiseResolve(« _value_ »). + 1. Let _valueWrapper_ be ? PromiseResolve(%Promise%, « _value_ »). 1. Let _steps_ be the algorithm steps defined in . 1. Let _onFulfilled_ be CreateBuiltinFunction(_steps_, « [[Done]] »). 1. Set _onFulfilled_.[[Done]] to _done_. @@ -37533,7 +37533,7 @@

AsyncGeneratorResumeNext ( _generator_ )

1. If _state_ is `"completed"`, then 1. If _completion_.[[Type]] is ~return~, then 1. Set _generator_.[[AsyncGeneratorState]] to `"awaiting-return"`. - 1. Let _promise_ be ? PromiseResolve(« _completion_.[[Value]] »). + 1. Let _promise_ be ? PromiseResolve(%Promise%, « _completion_.[[Value]] »). 1. Let _stepsFulfilled_ be the algorithm steps defined in . 1. Let _onFulfilled_ be CreateBuiltinFunction(_stepsFulfilled_, « [[Generator]] »). 1. Set _onFulfilled_.[[Generator]] to _generator_. From e985e41c2ac1906ea69226f24966ba1a1f82340b Mon Sep 17 00:00:00 2001 From: Maya Lekova Date: Thu, 12 Jul 2018 14:50:49 +0200 Subject: [PATCH 3/3] Markup: Fix handle for clause AsyncFromSyncIteratorContinuation (#1250) Make the clause adhere to the naming policy as stated in this review comment [1]. [1] https://github.com/tc39/ecma262/pull/1248#issuecomment-403659005 --- spec.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.html b/spec.html index 8ed9279886..e4dee791ae 100644 --- a/spec.html +++ b/spec.html @@ -36900,7 +36900,7 @@

Properties of Async-from-Sync Iterator Instances

- +

AsyncFromSyncIteratorContinuation ( _result_, _promiseCapability_ )