diff --git a/polyfills/Promise/try/config.toml b/polyfills/Promise/try/config.toml new file mode 100644 index 00000000..d338dbcb --- /dev/null +++ b/polyfills/Promise/try/config.toml @@ -0,0 +1,30 @@ +aliases = [ "es2025" ] +dependencies = [ + "_ESAbstract.Call", + "_ESAbstract.CreateMethodProperty", + "_ESAbstract.NewPromiseCapability", + "_ESAbstract.NormalCompletion", + "_ESAbstract.ThrowCompletion", + "_ESAbstract.Type", + "Promise", +] +license = "MIT" +spec = "https://tc39.es/ecma262/#sec-promise.try" +docs = "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/try" + +[browsers] +android = "*" +bb = "*" +chrome = "<128" +edge = "*" +edge_mob = "*" +firefox = "<132" +firefox_mob = "*" +ie = "*" +ie_mob = "*" +opera = "<114" +op_mob = "*" +op_mini = "*" +safari = "<18.2" +ios_saf = "<18.2" +samsung_mob = "*" diff --git a/polyfills/Promise/try/detect.js b/polyfills/Promise/try/detect.js new file mode 100644 index 00000000..84be9c9b --- /dev/null +++ b/polyfills/Promise/try/detect.js @@ -0,0 +1 @@ +"Promise" in self && "try" in self.Promise; diff --git a/polyfills/Promise/try/polyfill.js b/polyfills/Promise/try/polyfill.js new file mode 100644 index 00000000..001f2289 --- /dev/null +++ b/polyfills/Promise/try/polyfill.js @@ -0,0 +1,56 @@ +/* global Call, CreateMethodProperty, NewPromiseCapability, NormalCompletion, Promise, ThrowCompletion, Type */ +// 27.2.4.8 Promise.try ( callback, ...args ) +CreateMethodProperty(Promise, "try", function Try(callback /* , ...args */) { + var args = + arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : []; + // 1. Let C be the this value. + var C = this; + // 2. If C is not an Object, throw a TypeError exception. + if (Type(C) !== "object") { + throw new TypeError("`this` value must be an object"); + } + // 3. Let promiseCapability be ? NewPromiseCapability(C). + var promiseCapability = NewPromiseCapability(C); + // 4. Let status be Completion(Call(callback, undefined, args)). + var status; + try { + status = NormalCompletion(Call(callback, undefined, args)); + } catch (error) { + status = ThrowCompletion(error); + } + // 5. If status is an abrupt completion, then + if (status["[[Type]]"] === "throw") { + // a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »). + Call(promiseCapability["[[Reject]]"], undefined, [status["[[Value]]"]]); + } + // 6. Else, + else { + // a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »). + Call(promiseCapability["[[Resolve]]"], undefined, [status["[[Value]]"]]); + } + // 7. Return promiseCapability.[[Promise]]. + return promiseCapability["[[Promise]]"]; +}); + +(function () { + var supportsDefiningFunctionName = (function () { + var fn = function () {}; + try { + Object.defineProperty(fn, "name", { + value: "test" + }); + return true; + } catch (ignore) { + return false; + } + })(); + + if (supportsDefiningFunctionName) { + Object.defineProperty(Promise.try, "name", { + value: "try", + writable: false, + enumerable: false, + configurable: true + }); + } +})(); diff --git a/polyfills/Promise/try/polyfill.test.js b/polyfills/Promise/try/polyfill.test.js new file mode 100644 index 00000000..64ea7a61 --- /dev/null +++ b/polyfills/Promise/try/polyfill.test.js @@ -0,0 +1,110 @@ +/* global Promise */ +var supportsDefiningFunctionName = (function () { + var fn = function () {}; + try { + Object.defineProperty(fn, "name", { + value: "test" + }); + return true; + } catch (ignore) { + return false; + } +})(); + +it("is a function", function () { + proclaim.isFunction(Promise.try); +}); + +it("has correct arity", function () { + proclaim.arity(Promise.try, 1); +}); + +it("has correct name", function () { + if (supportsDefiningFunctionName) { + proclaim.hasName(Promise.try, "try"); + } else { + proclaim.hasName(Promise.try, "Try"); + } +}); + +it("is not enumerable", function () { + proclaim.isNotEnumerable(Promise, "try"); +}); + +describe("try", function () { + it("returns a Promise that resolves for a callback function that returns a value", function () { + var promise = Promise.try(function () { + return "ok"; + }); + return promise.then(function (value) { + proclaim.equal(value, "ok"); + }); + }); + + it("returns a Promise that rejects for a callback function that throws synchronously", function () { + var promise = Promise.try(function () { + throw "not ok"; + }); + return promise.then( + function () { + proclaim.fail("promise did not reject"); + }, + function (error) { + proclaim.equal(error, "not ok"); + } + ); + }); + + it("returns a Promise that resolves for a callback function that returns a Promise that resolves", function () { + var promise = Promise.try(function () { + return Promise.resolve("ok"); + }); + return promise.then(function (value) { + proclaim.equal(value, "ok"); + }); + }); + + it("returns a Promise that rejects for a callback function that returns a Promise that rejects", function () { + var promise = Promise.try(function () { + return Promise.reject("not ok"); + }); + return promise.then( + function () { + proclaim.fail("promise did not reject"); + }, + function (error) { + proclaim.equal(error, "not ok"); + } + ); + }); + + it("calls the callback function with variadic arguments", function () { + var promise = Promise.try( + function (a, b) { + return a + b; + }, + 1, + 2 + ); + return promise.then(function (value) { + proclaim.equal(value, 3); + }); + }); + + it("throws a TypeError for non-constructor `this`", function () { + proclaim.throws(function () { + Promise.try.call({}); + }, TypeError); + }); + + it("supports `this` as a Promise subclass", function () { + function _Promise(executor) { + return new Promise(executor); + } + _Promise.prototype = Promise.prototype; + + var _promise = Promise.try.call(_Promise, function () {}); + + proclaim.ok(_promise instanceof _Promise); + }); +});