Bladeren bron

LibJS: Implement the Promise.withResolvers proposal

https://github.com/tc39/proposal-promise-with-resolvers
Timothy Flynn 2 jaren geleden
bovenliggende
commit
36d156428b

+ 2 - 0
Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h

@@ -405,6 +405,7 @@ namespace JS {
     P(pop)                                   \
     P(pop)                                   \
     P(pow)                                   \
     P(pow)                                   \
     P(preventExtensions)                     \
     P(preventExtensions)                     \
+    P(promise)                               \
     P(propertyIsEnumerable)                  \
     P(propertyIsEnumerable)                  \
     P(prototype)                             \
     P(prototype)                             \
     P(proxy)                                 \
     P(proxy)                                 \
@@ -582,6 +583,7 @@ namespace JS {
     P(withCalendar)                          \
     P(withCalendar)                          \
     P(withPlainDate)                         \
     P(withPlainDate)                         \
     P(withPlainTime)                         \
     P(withPlainTime)                         \
+    P(withResolvers)                         \
     P(withTimeZone)                          \
     P(withTimeZone)                          \
     P(writable)                              \
     P(writable)                              \
     P(year)                                  \
     P(year)                                  \

+ 28 - 0
Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp

@@ -259,6 +259,7 @@ ThrowCompletionOr<void> PromiseConstructor::initialize(Realm& realm)
     define_native_function(realm, vm.names.race, race, 1, attr);
     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.reject, reject, 1, attr);
     define_native_function(realm, vm.names.resolve, resolve, 1, attr);
     define_native_function(realm, vm.names.resolve, resolve, 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);
     define_native_accessor(realm, vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable);
 
 
@@ -487,4 +488,31 @@ JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::symbol_species_getter)
     return vm.this_value();
     return vm.this_value();
 }
 }
 
 
+// 1.1.1.1 Promise.withResolvers ( ), https://tc39.es/proposal-promise-with-resolvers/#sec-promise.withResolvers
+JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::with_resolvers)
+{
+    auto& realm = *vm.current_realm();
+
+    // 1. Let C be the this value.
+    auto constructor = vm.this_value();
+
+    // 2. Let promiseCapability be ? NewPromiseCapability(C).
+    auto promise_capability = TRY(new_promise_capability(vm, constructor));
+
+    // 3. Let obj be OrdinaryObjectCreate(%Object.prototype%).
+    auto object = Object::create(realm, realm.intrinsics().object_prototype());
+
+    // 4. Perform ! CreateDataPropertyOrThrow(obj, "promise", promiseCapability.[[Promise]]).
+    MUST(object->create_data_property_or_throw(vm.names.promise, promise_capability->promise()));
+
+    // 5. Perform ! CreateDataPropertyOrThrow(obj, "resolve", promiseCapability.[[Resolve]]).
+    MUST(object->create_data_property_or_throw(vm.names.resolve, promise_capability->resolve()));
+
+    // 6. Perform ! CreateDataPropertyOrThrow(obj, "reject", promiseCapability.[[Reject]]).
+    MUST(object->create_data_property_or_throw(vm.names.reject, promise_capability->reject()));
+
+    // 7. Return obj.
+    return object;
+}
+
 }
 }

+ 1 - 1
Userland/Libraries/LibJS/Runtime/PromiseConstructor.h

@@ -31,8 +31,8 @@ private:
     JS_DECLARE_NATIVE_FUNCTION(race);
     JS_DECLARE_NATIVE_FUNCTION(race);
     JS_DECLARE_NATIVE_FUNCTION(reject);
     JS_DECLARE_NATIVE_FUNCTION(reject);
     JS_DECLARE_NATIVE_FUNCTION(resolve);
     JS_DECLARE_NATIVE_FUNCTION(resolve);
-
     JS_DECLARE_NATIVE_FUNCTION(symbol_species_getter);
     JS_DECLARE_NATIVE_FUNCTION(symbol_species_getter);
+    JS_DECLARE_NATIVE_FUNCTION(with_resolvers);
 };
 };
 
 
 }
 }

+ 56 - 0
Userland/Libraries/LibJS/Tests/builtins/Promise/Promise.withResolvers.js

@@ -0,0 +1,56 @@
+describe("errors", () => {
+    test("this value must be a constructor", () => {
+        expect(() => {
+            Promise.withResolvers.call(Symbol.hasInstance);
+        }).toThrowWithMessage(TypeError, "Symbol(Symbol.hasInstance) is not a constructor");
+    });
+});
+
+describe("normal behavior", () => {
+    test("length is 0", () => {
+        expect(Promise.withResolvers).toHaveLength(0);
+    });
+
+    test("returned promise is a Promise", () => {
+        const { promise, resolve, reject } = Promise.withResolvers();
+        expect(promise).toBeInstanceOf(Promise);
+    });
+
+    test("returned resolve/reject are unary functions", () => {
+        const { promise, resolve, reject } = Promise.withResolvers();
+
+        expect(resolve).toBeInstanceOf(Function);
+        expect(resolve).toHaveLength(1);
+
+        expect(reject).toBeInstanceOf(Function);
+        expect(reject).toHaveLength(1);
+    });
+
+    test("returned promise can be resolved", () => {
+        const { promise, resolve, reject } = Promise.withResolvers();
+
+        let fulfillmentValue = null;
+        promise.then(value => {
+            fulfillmentValue = value;
+        });
+
+        resolve("Some value");
+        runQueuedPromiseJobs();
+
+        expect(fulfillmentValue).toBe("Some value");
+    });
+
+    test("returned promise can be rejected", () => {
+        const { promise, resolve, reject } = Promise.withResolvers();
+
+        let rejectionReason = null;
+        promise.catch(value => {
+            rejectionReason = value;
+        });
+
+        reject("Some value");
+        runQueuedPromiseJobs();
+
+        expect(rejectionReason).toBe("Some value");
+    });
+});