Skip to content
Permalink
Browse files
[JSC] Optimize Promise.all
https://bugs.webkit.org/show_bug.cgi?id=244239

Reviewed by Ross Kirsling.

This patch optimizes Promise.all. Previously we are always creating a promise per element.
Also, we are allocating closure which resolves promise returned by Promise.all.

1. By using promise resolution context mechanism, we no longer allocate closures.
2. By checking returned promise from Promise.resolve carefully, we avoid creating a promise,
   which is not visible to users.

We observe performance improvement in promise microbenchmarks.

    ToT
    Time(parallel-async-es2017-native): 42.6 ms.
    Time(parallel-promises-es2015-native): 32.1 ms.

    Patched
    Time(parallel-async-es2017-native): 32.9 ms.
    Time(parallel-promises-es2015-native): 23.1 ms.

* Source/JavaScriptCore/builtins/PromiseConstructor.js:
(linkTimeConstant.promiseOnRejectedWithContext):
(linkTimeConstant.promiseAllOnFulfilled):
(linkTimeConstant.promiseNewOnRejected.return.reject):
(linkTimeConstant.promiseNewOnRejected):
(linkTimeConstant.promiseAllNewResolveElement):
(all):
(all.newResolveElement): Deleted.

Canonical link: https://commits.webkit.org/253716@main
  • Loading branch information
Constellation committed Aug 24, 2022
1 parent c2af502 commit 4136973865587fb6adf30abb4ca51fdfae868aed
Showing 1 changed file with 103 additions and 1 deletion.
@@ -23,7 +23,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

function all(iterable)
@linkTimeConstant
function promiseAllSlow(iterable)
{
"use strict";

@@ -78,6 +79,107 @@ function all(iterable)
return promiseCapability.@promise;
}

@linkTimeConstant
function promiseOnRejectedWithContext(argument, context)
{
"use strict";

return @rejectPromiseWithFirstResolvingFunctionCallCheck(context.globalContext.promise, argument);
}

@linkTimeConstant
function promiseAllOnFulfilled(argument, context)
{
"use strict";

var globalContext = context.globalContext;
var values = globalContext.values;

@putByValDirect(values, context.index, argument);

if (!--globalContext.remainingElementsCount)
return @resolvePromiseWithFirstResolvingFunctionCallCheck(globalContext.promise, values);
}

@linkTimeConstant
function promiseNewOnRejected(promise)
{
"use strict";

return function @reject(reason) {
return @rejectPromiseWithFirstResolvingFunctionCallCheck(promise, reason);
};
}

@linkTimeConstant
function promiseAllNewResolveElement(globalContext, index)
{
"use strict";

var alreadyCalled = false;
return (argument) => {
if (alreadyCalled)
return @undefined;
alreadyCalled = true;

var values = globalContext.values;
@putByValDirect(values, index, argument);

if (!--globalContext.remainingElementsCount)
return @resolvePromiseWithFirstResolvingFunctionCallCheck(globalContext.promise, values);
};
}

function all(iterable)
{
"use strict";

if (this !== @Promise)
return @tailCallForwardArguments(@promiseAllSlow, this);

var promise = @newPromise();
var values = [];
var globalContext = {
promise,
values,
remainingElementsCount: 1,
};
var index = 0;
var onRejected;

try {
var promiseResolve = this.resolve;
if (!@isCallable(promiseResolve))
@throwTypeError("Promise resolve is not a function");

for (var value of iterable) {
@putByValDirect(values, index, @undefined);
var nextPromise = promiseResolve.@call(this, value);
++globalContext.remainingElementsCount;
var then = nextPromise.then;
if (@isPromise(nextPromise) && then === @defaultPromiseThen) {
var constructor = @speciesConstructor(nextPromise, @Promise);
var promiseOrCapability;
if (constructor !== @Promise)
promiseOrCapability = @newPromiseCapabilitySlow(constructor);
@performPromiseThen(nextPromise, @promiseAllOnFulfilled, @promiseOnRejectedWithContext, promiseOrCapability, { globalContext, index });
} else {
if (!onRejected)
onRejected = @promiseNewOnRejected(promise);
then.@call(nextPromise, @promiseAllNewResolveElement(globalContext, index), onRejected);
}
++index;
}

if (!--globalContext.remainingElementsCount)
@resolvePromiseWithFirstResolvingFunctionCallCheck(promise, values);
} catch (error) {
@rejectPromiseWithFirstResolvingFunctionCallCheck(promise, error);
}

return promise;
}

function allSettled(iterable)
{
"use strict";

0 comments on commit 4136973

Please sign in to comment.