Skip to content

Commit

Permalink
Remove Job from Promise Resolve Functions
Browse files Browse the repository at this point in the history
This aligns the behavior of handling thenables with the Promises A+ spec, which says when the resolution value is a thenable, you're supposed to synchronously call `thenable.then(onRes, onRej)` ([Step 2.3.3.3][call-then]).

Given the example code:

```javascript
new Promise(resolve => {
  resolve(thenable)
});
```

The current behavior requires 2 ticks for the outer promise to fully resolve. One tick is created tick is created by the Promise Resolving Functions (and is removed in this PR), and one tick is created by the wrapping of the `onRes`/`onRej` callbacks passed to [`Promise.p.then`][then]. This made it noticeably slower to resolve with a thenable then to invoke the thenable's `.then` directly: `thenable.then(onRes, onRej)` only requires a single tick (for the wrapped `onRes`/`onRej`).

With this change, we could revert tc39#1250 without slowing down `Await`.

Fixes tc39#2770.

[call-then]: https://promisesaplus.com/#point-56
[then]: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-performpromisethen
  • Loading branch information
jridgewell committed May 9, 2022
1 parent 656f0cf commit 4e7ffb3
Showing 1 changed file with 5 additions and 33 deletions.
38 changes: 5 additions & 33 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -12126,7 +12126,7 @@ <h1>
</ul>

<emu-note>
<p>The _realm_ for Jobs returned by NewPromiseResolveThenableJob is usually the result of calling GetFunctionRealm on the _then_ function object. The _realm_ for Jobs returned by NewPromiseReactionJob is usually the result of calling GetFunctionRealm on the handler if the handler is not *undefined*. If the handler is *undefined*, _realm_ is *null*. For both kinds of Jobs, when GetFunctionRealm completes abnormally (i.e. called on a revoked Proxy), _realm_ is the current Realm at the time of the GetFunctionRealm call. When the _realm_ is *null*, no user ECMAScript code will be evaluated and no new ECMAScript objects (e.g. Error objects) will be created. The WHATWG HTML specification (<a href="https://html.spec.whatwg.org/">https://html.spec.whatwg.org/</a>), for example, uses _realm_ to check for the ability to run script and for the <a href="https://html.spec.whatwg.org/#entry">entry</a> concept.</p>
<p>The _realm_ for Jobs returned by NewPromiseReactionJob is usually the result of calling GetFunctionRealm on the handler if the handler is not *undefined*. If the handler is *undefined*, _realm_ is *null*. When GetFunctionRealm completes abnormally (i.e. called on a revoked Proxy), _realm_ is the current Realm at the time of the GetFunctionRealm call. When the _realm_ is *null*, no user ECMAScript code will be evaluated and no new ECMAScript objects (e.g. Error objects) will be created. The WHATWG HTML specification (<a href="https://html.spec.whatwg.org/">https://html.spec.whatwg.org/</a>), for example, uses _realm_ to check for the ability to run script and for the <a href="https://html.spec.whatwg.org/#entry">entry</a> concept.</p>
</emu-note>
</emu-clause>
</emu-clause>
Expand Down Expand Up @@ -43597,9 +43597,10 @@ <h1>Promise Resolve Functions</h1>
1. If IsCallable(_thenAction_) is *false*, then
1. Perform FulfillPromise(_promise_, _resolution_).
1. Return *undefined*.
1. Let _thenJobCallback_ be HostMakeJobCallback(_thenAction_).
1. Let _job_ be NewPromiseResolveThenableJob(_promise_, _resolution_, _thenJobCallback_).
1. Perform HostEnqueuePromiseJob(_job_.[[Job]], _job_.[[Realm]]).
1. Let _resolvingFunctions_ be CreateResolvingFunctions(_promise_).
1. Let _thenCallResult_ be Completion(Call(_then_, _resolution_, &laquo; _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] &raquo;)).
1. If _thenCallResult_ is an abrupt completion, then
1. Return Call(_resolvingFunctions_.[[Reject]], *undefined*, &laquo; _thenCallResult_.[[Value]] &raquo;).
1. Return *undefined*.
</emu-alg>
<p>The *"length"* property of a promise resolve function is *1*<sub>𝔽</sub>.</p>
Expand Down Expand Up @@ -43794,35 +43795,6 @@ <h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-newpromiseresolvethenablejob" type="abstract operation" oldids="sec-promiseresolvethenablejob">
<h1>
NewPromiseResolveThenableJob (
_promiseToResolve_: unknown,
_thenable_: unknown,
_then_: unknown,
): a Record with fields [[Job]] (a Job Abstract Closure) and [[Realm]] (a Realm Record)
</h1>
<dl class="header">
</dl>
<emu-alg>
1. Let _job_ be a new Job Abstract Closure with no parameters that captures _promiseToResolve_, _thenable_, and _then_ and performs the following steps when called:
1. Let _resolvingFunctions_ be CreateResolvingFunctions(_promiseToResolve_).
1. Let _thenCallResult_ be Completion(HostCallJobCallback(_then_, _thenable_, &laquo; _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] &raquo;)).
1. If _thenCallResult_ is an abrupt completion, then
1. Return ? Call(_resolvingFunctions_.[[Reject]], *undefined*, &laquo; _thenCallResult_.[[Value]] &raquo;).
1. Return ? _thenCallResult_.
1. Let _getThenRealmResult_ be Completion(GetFunctionRealm(_then_.[[Callback]])).
1. If _getThenRealmResult_ is a normal completion, let _thenRealm_ be _getThenRealmResult_.[[Value]].
1. Else, let _thenRealm_ be the current Realm Record.
1. NOTE: _thenRealm_ is never *null*. When _then_.[[Callback]] is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects.
1. Return the Record { [[Job]]: _job_, [[Realm]]: _thenRealm_ }.
</emu-alg>
<emu-note>
<p>This Job uses the supplied thenable and its `then` method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the `then` method occurs after evaluation of any surrounding code has completed.</p>
</emu-note>
</emu-clause>
</emu-clause>

<emu-clause id="sec-promise-constructor">
<h1>The Promise Constructor</h1>
<p>The Promise constructor:</p>
Expand Down

0 comments on commit 4e7ffb3

Please sign in to comment.