mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 13:00:29 +03:00
LibJS: Implement Promise.try()
See: https://github.com/tc39/proposal-promise-try
This commit is contained in:
parent
2317a8a4eb
commit
cad95ce274
Notes:
sideshowbarker
2024-07-17 08:35:21 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/cad95ce274 Pull-request: https://github.com/SerenityOS/serenity/pull/23898 Reviewed-by: https://github.com/kennethmyhra ✅
@ -614,6 +614,7 @@ struct CommonPropertyNames {
|
||||
PropertyKey register_ { "register", PropertyKey::StringMayBeNumber::No };
|
||||
PropertyKey return_ { "return", PropertyKey::StringMayBeNumber::No };
|
||||
PropertyKey throw_ { "throw", PropertyKey::StringMayBeNumber::No };
|
||||
PropertyKey try_ { "try", PropertyKey::StringMayBeNumber::No };
|
||||
PropertyKey union_ { "union", PropertyKey::StringMayBeNumber::No };
|
||||
PropertyKey xor_ { "xor", PropertyKey::StringMayBeNumber::No };
|
||||
PropertyKey inputAlias { "$_", PropertyKey::StringMayBeNumber::No };
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021-2024, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
@ -238,6 +238,7 @@ void PromiseConstructor::initialize(Realm& realm)
|
||||
define_native_function(realm, vm.names.race, race, 1, attr);
|
||||
define_native_function(realm, vm.names.reject, reject, 1, attr);
|
||||
define_native_function(realm, vm.names.resolve, resolve, 1, attr);
|
||||
define_native_function(realm, vm.names.try_, try_, 1, attr);
|
||||
define_native_function(realm, vm.names.withResolvers, with_resolvers, 0, attr);
|
||||
|
||||
define_native_accessor(realm, vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable);
|
||||
@ -458,6 +459,43 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::resolve)
|
||||
return TRY(promise_resolve(vm, constructor.as_object(), value));
|
||||
}
|
||||
|
||||
// 1 Promise.try ( callbackfn, ...args ), https://tc39.es/proposal-promise-try/#sec-promise.try
|
||||
JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::try_)
|
||||
{
|
||||
auto callbackfn = vm.argument(0);
|
||||
Span<Value> args;
|
||||
if (vm.argument_count() > 1) {
|
||||
args = vm.running_execution_context().arguments.span().slice(1, vm.argument_count() - 1);
|
||||
}
|
||||
|
||||
// 1. Let C be the this value.
|
||||
auto constructor = vm.this_value();
|
||||
|
||||
// 2. If C is not an Object, throw a TypeError exception.
|
||||
if (!constructor.is_object())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, constructor.to_string_without_side_effects());
|
||||
|
||||
// 3. Let promiseCapability be ? NewPromiseCapability(C).
|
||||
auto promise_capability = TRY(new_promise_capability(vm, constructor));
|
||||
|
||||
// 4. Let status be Completion(Call(callbackfn, undefined, args)).
|
||||
auto status = JS::call(vm, callbackfn, js_undefined(), args);
|
||||
|
||||
// 5. If status is an abrupt completion, then
|
||||
if (status.is_throw_completion()) {
|
||||
// a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »).
|
||||
TRY(JS::call(vm, *promise_capability->reject(), js_undefined(), *status.throw_completion().value()));
|
||||
}
|
||||
// 6. Else,
|
||||
else {
|
||||
// a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »).
|
||||
TRY(JS::call(vm, *promise_capability->resolve(), js_undefined(), status.value()));
|
||||
}
|
||||
|
||||
// 7. Return promiseCapability.[[Promise]].
|
||||
return promise_capability->promise();
|
||||
}
|
||||
|
||||
// 27.2.4.8 Promise.withResolvers ( ), https://tc39.es/ecma262/#sec-promise.withResolvers
|
||||
JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::with_resolvers)
|
||||
{
|
||||
|
@ -33,6 +33,7 @@ private:
|
||||
JS_DECLARE_NATIVE_FUNCTION(reject);
|
||||
JS_DECLARE_NATIVE_FUNCTION(resolve);
|
||||
JS_DECLARE_NATIVE_FUNCTION(symbol_species_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(try_);
|
||||
JS_DECLARE_NATIVE_FUNCTION(with_resolvers);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
describe("errors", () => {
|
||||
test("this value must be a constructor", () => {
|
||||
expect(() => {
|
||||
Promise.try.call({});
|
||||
}).toThrowWithMessage(TypeError, "[object Object] is not a constructor");
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
test("length is 1", () => {
|
||||
expect(Promise.try).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("returned promise is a Promise", () => {
|
||||
const fn = () => {};
|
||||
const promise = Promise.try(fn);
|
||||
expect(promise).toBeInstanceOf(Promise);
|
||||
});
|
||||
|
||||
test("returned promise is resolved when function completes normally", () => {
|
||||
const fn = () => {};
|
||||
const promise = Promise.try(fn);
|
||||
|
||||
let fulfillmentValue = null;
|
||||
promise.then(value => {
|
||||
fulfillmentValue = value;
|
||||
});
|
||||
|
||||
runQueuedPromiseJobs();
|
||||
|
||||
expect(fulfillmentValue).toBe(undefined);
|
||||
});
|
||||
|
||||
test("returned promise is rejected when function throws", () => {
|
||||
const fn = () => {
|
||||
throw "error";
|
||||
};
|
||||
const promise = Promise.try(fn);
|
||||
|
||||
let rejectionReason = null;
|
||||
promise.catch(value => {
|
||||
rejectionReason = value;
|
||||
});
|
||||
|
||||
runQueuedPromiseJobs();
|
||||
|
||||
expect(rejectionReason).toBe("error");
|
||||
});
|
||||
|
||||
test("arguments are forwarded to the function", () => {
|
||||
const fn = (...args) => args;
|
||||
const promise = Promise.try(fn, "foo", 123, true);
|
||||
|
||||
let fulfillmentValue = null;
|
||||
promise.then(value => {
|
||||
fulfillmentValue = value;
|
||||
});
|
||||
|
||||
runQueuedPromiseJobs();
|
||||
|
||||
expect(fulfillmentValue).toEqual(["foo", 123, true]);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user