From 13e0e13d0e14cf3057f4d723b77a03f90f291ccb Mon Sep 17 00:00:00 2001 From: uzlopak Date: Sun, 17 Mar 2024 15:13:20 +0100 Subject: [PATCH] adapt changes from #34862 --- lib/internal/process/promises.js | 31 +++++++++++-------- .../test-promise-unhandled-issue-43655.js | 26 ++++++++++++++++ 2 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 test/parallel/test-promise-unhandled-issue-43655.js diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index dff7c35dbd1cde..60ad7d40911845 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -5,6 +5,7 @@ const { ArrayPrototypeShift, Error, ObjectPrototypeHasOwnProperty, + SafeMap, SafeWeakMap, } = primordials; @@ -149,9 +150,10 @@ class PromiseRejectionHandledWarning extends Error { const maybeUnhandledPromises = new SafeWeakMap(); /** - * @type {Promise[]} + * Using a Mp causes the promise to be referenced at least for one tick. + * @type {Map} */ -const pendingUnhandledRejections = []; +let pendingUnhandledRejections = new SafeMap(); /** * @type {Array<{promise: Promise, warning: Error}>} @@ -279,14 +281,12 @@ const emitUnhandledRejection = (promise, promiseInfo) => { * @param {Error} reason */ function unhandledRejection(promise, reason) { - maybeUnhandledPromises.set(promise, { + pendingUnhandledRejections.set(promise, { reason, uid: ++lastPromiseId, warned: false, domain: process.domain, }); - // This causes the promise to be referenced at least for one tick. - ArrayPrototypePush(pendingUnhandledRejections, promise); setHasRejectionToWarn(true); } @@ -294,6 +294,10 @@ function unhandledRejection(promise, reason) { * @param {Promise} promise */ function handledRejection(promise) { + if (pendingUnhandledRejections.has(promise)) { + pendingUnhandledRejections.delete(promise); + return; + } const promiseInfo = maybeUnhandledPromises.get(promise); if (promiseInfo !== undefined) { maybeUnhandledPromises.delete(promise); @@ -465,16 +469,17 @@ function processPromiseRejections() { } } - let len = pendingUnhandledRejections.length; let needPop = true; let promiseAsyncId; - while (len--) { - const promise = ArrayPrototypeShift(pendingUnhandledRejections); - const promiseInfo = maybeUnhandledPromises.get(promise); - if (promiseInfo === undefined) { - continue; - } + const pending = pendingUnhandledRejections; + pendingUnhandledRejections = new SafeMap(); + + for (const { 0: promise, 1: promiseInfo } of pending.entries()) { + pending.delete(promise); + + maybeUnhandledPromises.set(promise, promiseInfo); + promiseInfo.warned = true; // We need to check if async_hooks are enabled @@ -499,7 +504,7 @@ function processPromiseRejections() { maybeScheduledTicksOrMicrotasks = true; } return maybeScheduledTicksOrMicrotasks || - pendingUnhandledRejections.length !== 0; + pendingUnhandledRejections.size !== 0; } function listenForRejections() { diff --git a/test/parallel/test-promise-unhandled-issue-43655.js b/test/parallel/test-promise-unhandled-issue-43655.js new file mode 100644 index 00000000000000..f2c93a5cd36771 --- /dev/null +++ b/test/parallel/test-promise-unhandled-issue-43655.js @@ -0,0 +1,26 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +function delay(time) { + return new Promise((resolve) => { + setTimeout(resolve, time); + }); +} + +async function test() { + for (let i = 0; i < 1000000; i++) { + await new Promise((resolve, reject) => { + reject('value'); + }) + .then(() => { }, () => { }); + } + + const time0 = Date.now(); + await delay(0); + + assert.ok(Date.now() - time0 < 10); +} + +test();