From f26cd0bce2d454ade4b1f9c3d631c9ae287505ef Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Wed, 5 May 2021 15:27:15 +0300 Subject: [PATCH] native promise-based APIs `Promise#{ catch, finally }` returns polyfilled `Promise` instances when it's required --- CHANGELOG.md | 2 +- .../core-js/modules/es.promise.finally.js | 9 ++++-- packages/core-js/modules/es.promise.js | 29 ++++++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4278df546e4..729c9f09546e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Changelog ##### Unreleased -- Nothing +- Native promise-based APIs `Promise#{ catch, finally }` returns polyfilled `Promise` instances when it's required ##### 3.11.2 - 2021.05.03 - Added a workaround of WebKit ~ iOS 10.3 Safari `Promise` bug, [#932](https://github.com/zloirock/core-js/issues/932) diff --git a/packages/core-js/modules/es.promise.finally.js b/packages/core-js/modules/es.promise.finally.js index fb5fea7fccb1..98b0fefc1315 100644 --- a/packages/core-js/modules/es.promise.finally.js +++ b/packages/core-js/modules/es.promise.finally.js @@ -30,7 +30,10 @@ $({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, { } }); -// patch native Promise.prototype for native async functions -if (!IS_PURE && typeof NativePromise == 'function' && !NativePromise.prototype['finally']) { - redefine(NativePromise.prototype, 'finally', getBuiltIn('Promise').prototype['finally']); +// makes sure that native promise-based APIs `Promise#finally` properly works with patched `Promise#then` +if (!IS_PURE && typeof NativePromise == 'function') { + var method = getBuiltIn('Promise').prototype['finally']; + if (NativePromise.prototype['finally'] !== method) { + redefine(NativePromise.prototype, 'finally', method, { unsafe: true }); + } } diff --git a/packages/core-js/modules/es.promise.js b/packages/core-js/modules/es.promise.js index f8e0579e4bfd..bb0417ce3bfe 100644 --- a/packages/core-js/modules/es.promise.js +++ b/packages/core-js/modules/es.promise.js @@ -36,6 +36,7 @@ var setInternalState = InternalStateModule.set; var getInternalPromiseState = InternalStateModule.getterFor(PROMISE); var NativePromisePrototype = NativePromise && NativePromise.prototype; var PromiseConstructor = NativePromise; +var PromiseConstructorPrototype = NativePromisePrototype; var TypeError = global.TypeError; var document = global.document; var process = global.process; @@ -60,7 +61,7 @@ var FORCED = isForced(PROMISE, function () { // We can't detect it synchronously, so just check versions if (!GLOBAL_CORE_JS_PROMISE && V8_VERSION === 66) return true; // We need Promise#finally in the pure version for preventing prototype pollution - if (IS_PURE && !PromiseConstructor.prototype['finally']) return true; + if (IS_PURE && !PromiseConstructorPrototype['finally']) return true; // We can't use @@species feature detection in V8 since it causes // deoptimization and performance degradation // https://github.com/zloirock/core-js/issues/679 @@ -239,6 +240,7 @@ if (FORCED) { internalReject(state, error); } }; + PromiseConstructorPrototype = PromiseConstructor.prototype; // eslint-disable-next-line no-unused-vars -- required for `.length` Internal = function Promise(executor) { setInternalState(this, { @@ -252,7 +254,7 @@ if (FORCED) { value: undefined }); }; - Internal.prototype = redefineAll(PromiseConstructor.prototype, { + Internal.prototype = redefineAll(PromiseConstructorPrototype, { // `Promise.prototype.then` method // https://tc39.es/ecma262/#sec-promise.prototype.then then: function then(onFulfilled, onRejected) { @@ -288,14 +290,19 @@ if (FORCED) { if (!IS_PURE && typeof NativePromise == 'function' && NativePromisePrototype !== Object.prototype) { nativeThen = NativePromisePrototype.then; - // make `Promise#then` return a polyfilled `Promise` for native promise-based APIs - if (!SUBCLASSING) redefine(NativePromisePrototype, 'then', function then(onFulfilled, onRejected) { - var that = this; - return new PromiseConstructor(function (resolve, reject) { - nativeThen.call(that, resolve, reject); - }).then(onFulfilled, onRejected); - // https://github.com/zloirock/core-js/issues/640 - }, { unsafe: true }); + if (!SUBCLASSING) { + // make `Promise#then` return a polyfilled `Promise` for native promise-based APIs + redefine(NativePromisePrototype, 'then', function then(onFulfilled, onRejected) { + var that = this; + return new PromiseConstructor(function (resolve, reject) { + nativeThen.call(that, resolve, reject); + }).then(onFulfilled, onRejected); + // https://github.com/zloirock/core-js/issues/640 + }, { unsafe: true }); + + // makes sure that native promise-based APIs `Promise#catch` properly works with patched `Promise#then` + redefine(NativePromisePrototype, 'catch', PromiseConstructorPrototype['catch'], { unsafe: true }); + } // make `.constructor === Promise` work for native promise-based APIs try { @@ -304,7 +311,7 @@ if (FORCED) { // make `instanceof Promise` work for native promise-based APIs if (setPrototypeOf) { - setPrototypeOf(NativePromisePrototype, PromiseConstructor.prototype); + setPrototypeOf(NativePromisePrototype, PromiseConstructorPrototype); } } }