Add Promise.prototype.finally().

This commit is contained in:
Fedor 2019-12-25 15:45:53 +03:00
parent b8c8d932f2
commit adaffdd732
5 changed files with 105 additions and 1 deletions

View File

@ -2006,6 +2006,13 @@ CommonStaticResolveRejectImpl(JSContext* cx, HandleValue thisVal, HandleValue ar
return promise;
}
MOZ_MUST_USE JSObject*
js::PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value)
{
RootedValue C(cx, ObjectValue(*constructor));
return CommonStaticResolveRejectImpl(cx, C, value, ResolveMode);
}
/**
* ES2016, 25.4.4.4, Promise.reject.
*/
@ -2739,6 +2746,7 @@ CreatePromisePrototype(JSContext* cx, JSProtoKey key)
static const JSFunctionSpec promise_methods[] = {
JS_SELF_HOSTED_FN("catch", "Promise_catch", 1, 0),
JS_FN("then", Promise_then, 2, 0),
JS_SELF_HOSTED_FN("finally", "Promise_finally", 1, 0),
JS_FS_END
};

View File

@ -128,6 +128,14 @@ OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
HandleValue onFulfilled, HandleValue onRejected,
MutableHandleObject dependent, bool createDependent);
/**
* PromiseResolve ( C, x )
*
* The abstract operation PromiseResolve, given a constructor and a value,
* returns a new promise resolved with that value.
*/
MOZ_MUST_USE JSObject*
PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value);
MOZ_MUST_USE PromiseObject*
CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal);

View File

@ -14,3 +14,72 @@ function Promise_catch(onRejected) {
// Steps 1-2.
return callContentFunction(this.then, this, undefined, onRejected);
}
// Promise.prototype.finally(onFinally)
// See https://tc39.es/proposal-promise-finally/
function Promise_finally(onFinally) {
// Step 1.
var promise = this;
// Step 2.
if (!IsObject(promise))
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Promise", "finally", "value");
// Step 3.
var C = SpeciesConstructor(promise, GetBuiltinConstructor("Promise"));
// Step 4.
assert(IsConstructor(C), "SpeciesConstructor returns a constructor function");
// Steps 5-6.
var thenFinally, catchFinally;
if (!IsCallable(onFinally)) {
thenFinally = onFinally;
catchFinally = onFinally;
} else {
// ThenFinally Function.
// The parentheses prevent the inferring of a function name.
(thenFinally) = function(value) {
// Steps 1-2 (implicit).
// Step 3.
var result = onFinally();
// Steps 4-5 (implicit).
// Step 6.
var promise = PromiseResolve(C, result);
// Step 7.
// FIXME: spec issue - "be equivalent to a function that" is not a defined spec term.
// https://github.com/tc39/ecma262/issues/933
// Step 8.
return callContentFunction(promise.then, promise, function() { return value; });
};
// CatchFinally Function.
// The parentheses prevent the inferring of a function name.
(catchFinally) = function(reason) {
// Steps 1-2 (implicit).
// Step 3.
var result = onFinally();
// Steps 4-5 (implicit).
// Step 6.
var promise = PromiseResolve(C, result);
// Step 7.
// FIXME: spec issue - "be equivalent to a function that" is not a defined spec term.
// https://github.com/tc39/ecma262/issues/933
// Step 8.
return callContentFunction(promise.then, promise, function() { throw reason; });
};
}
// Step 7.
return callContentFunction(promise.then, promise, thenFinally, catchFinally);
}

View File

@ -2102,6 +2102,21 @@ intrinsic_ModuleNamespaceExports(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedObject constructor(cx, &args[0].toObject());
JSObject* promise = js::PromiseResolve(cx, constructor, args[1]);
if (!promise)
return false;
args.rval().setObject(*promise);
return true;
}
// The self-hosting global isn't initialized with the normal set of builtins.
// Instead, individual C++-implemented functions that're required by
// self-hosted code are defined as global functions. Accessing these
@ -2498,6 +2513,10 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
JS_FN("IsPromiseObject", intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1, 0),
JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<PromiseObject>>, 2, 0),
JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0),
JS_FS_END
};

View File

@ -243,7 +243,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
"$`", "$'", Symbol.species])
gPrototypeProperties['Promise'] =
["constructor", "catch", "then", Symbol.toStringTag];
["constructor", "catch", "then", "finally", Symbol.toStringTag];
gConstructorProperties['Promise'] =
constructorProps(["resolve", "reject", "all", "race", Symbol.species]);