From d693107d02eaec2779951b04756c31b8205a8349 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 23 Sep 2024 14:19:56 -0700 Subject: [PATCH] userId: fix unhandled rejection from refreshUserIds (#12246) * userId: fix unhandled rejection from refreshUserIds * apply same treatment to getEncryptedEidsForSource --- modules/userId/index.js | 40 ++++++++++++++++++-------------- test/spec/modules/userId_spec.js | 24 ++++++++++--------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index b274afda397..d0299427603 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -723,7 +723,7 @@ function getUserIdsAsEidBySource(sourceName) { * Sample use case is exposing this function to ESP */ function getEncryptedEidsForSource(source, encrypt, customFunction) { - return initIdSystem().then(() => { + return retryOnCancel().then(() => { let eidsSignals = {}; if (isFn(customFunction)) { @@ -782,6 +782,23 @@ function registerSignalSources() { } } +function retryOnCancel(initParams) { + return initIdSystem(initParams).then( + () => getUserIds(), + (e) => { + if (e === INIT_CANCELED) { + // there's a pending refresh - because GreedyPromise runs this synchronously, we are now in the middle + // of canceling the previous init, before the refresh logic has had a chance to run. + // Use a "normal" Promise to clear the stack and let it complete (or this will just recurse infinitely) + return Promise.resolve().then(getUserIdsAsync) + } else { + logError('Error initializing userId', e) + return GreedyPromise.reject(e) + } + } + ); +} + /** * Force (re)initialization of ID submodules. * @@ -793,12 +810,12 @@ function registerSignalSources() { * @param callback? called when the refresh is complete */ function refreshUserIds({submoduleNames} = {}, callback) { - return initIdSystem({refresh: true, submoduleNames}) - .then(() => { + return retryOnCancel({refresh: true, submoduleNames}) + .then((userIds) => { if (callback && isFn(callback)) { callback(); } - return getUserIds(); + return userIds; }); } @@ -814,20 +831,7 @@ function refreshUserIds({submoduleNames} = {}, callback) { */ function getUserIdsAsync() { - return initIdSystem().then( - () => getUserIds(), - (e) => { - if (e === INIT_CANCELED) { - // there's a pending refresh - because GreedyPromise runs this synchronously, we are now in the middle - // of canceling the previous init, before the refresh logic has had a chance to run. - // Use a "normal" Promise to clear the stack and let it complete (or this will just recurse infinitely) - return Promise.resolve().then(getUserIdsAsync) - } else { - logError('Error initializing userId', e) - return GreedyPromise.reject(e) - } - } - ); + return retryOnCancel(); } export function getConsentHash() { diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 3e659c867cf..02c70507a07 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1102,18 +1102,20 @@ describe('User ID', function () { }); }); - it('should still resolve promises returned by getUserIdsAsync', () => { - startInit(); - let result = null; - getGlobal().getUserIdsAsync().then((val) => { result = val; }); - return clearStack().then(() => { - expect(result).to.equal(null); // auction has not ended, callback should not have been called - mockIdCallback.callsFake((cb) => cb(MOCK_ID)); - return getGlobal().refreshUserIds().then(clearStack); - }).then(() => { - expect(result).to.deep.equal(getGlobal().getUserIds()) // auction still not over, but refresh was explicitly forced + ['refreshUserIds', 'getUserIdsAsync'].forEach(method => { + it(`should still resolve promises returned by ${method}`, () => { + startInit(); + let result = null; + getGlobal()[method]().then((val) => { result = val; }); + return clearStack().then(() => { + expect(result).to.equal(null); // auction has not ended, callback should not have been called + mockIdCallback.callsFake((cb) => cb(MOCK_ID)); + return getGlobal().refreshUserIds().then(clearStack); + }).then(() => { + expect(result).to.deep.equal(getGlobal().getUserIds()) // auction still not over, but refresh was explicitly forced + }); }); - }); + }) it('should not stop auctions', (done) => { // simulate an infinite `auctionDelay`; refreshing should still allow the auction to continue