From 4e7ffb3466efbb46b9b96790ca14caa8c3bd957b Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Mon, 9 May 2022 18:05:39 -0400 Subject: [PATCH] Remove Job from Promise Resolve Functions 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 #1250 without slowing down `Await`. Fixes #2770. [call-then]: https://promisesaplus.com/#point-56 [then]: https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-performpromisethen --- spec.html | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/spec.html b/spec.html index bc43d96f9f1..36d1e9d8347 100644 --- a/spec.html +++ b/spec.html @@ -12126,7 +12126,7 @@

-

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 (https://html.spec.whatwg.org/), for example, uses _realm_ to check for the ability to run script and for the entry concept.

+

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 (https://html.spec.whatwg.org/), for example, uses _realm_ to check for the ability to run script and for the entry concept.

@@ -43597,9 +43597,10 @@

Promise Resolve Functions

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_, « _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] »)). + 1. If _thenCallResult_ is an abrupt completion, then + 1. Return Call(_resolvingFunctions_.[[Reject]], *undefined*, « _thenCallResult_.[[Value]] »). 1. Return *undefined*.

The *"length"* property of a promise resolve function is *1*𝔽.

@@ -43794,35 +43795,6 @@

- -

- NewPromiseResolveThenableJob ( - _promiseToResolve_: unknown, - _thenable_: unknown, - _then_: unknown, - ): a Record with fields [[Job]] (a Job Abstract Closure) and [[Realm]] (a Realm Record) -

-
-
- - 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_, « _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] »)). - 1. If _thenCallResult_ is an abrupt completion, then - 1. Return ? Call(_resolvingFunctions_.[[Reject]], *undefined*, « _thenCallResult_.[[Value]] »). - 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_ }. - - -

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.

-
-
- -

The Promise Constructor

The Promise constructor: