Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions doc/api/deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -3332,6 +3332,9 @@ the errors used for value type validation.

<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/58707
description: End-of-Life.
- version: v18.0.0
pr-url: https://github.com/nodejs/node/pull/41896
description: Runtime deprecation.
Expand All @@ -3342,10 +3345,10 @@ changes:
description: Documentation-only deprecation.
-->

Type: Runtime
Type: End-of-Life

This event was deprecated because it did not work with V8 promise combinators
which diminished its usefulness.
This event was deprecated and removed because it did not work with V8 promise
combinators which diminished its usefulness.

### DEP0161: `process._getActiveRequests()` and `process._getActiveHandles()`

Expand Down
90 changes: 0 additions & 90 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,95 +176,6 @@ process, the `message` argument can contain data that JSON is not able
to represent.
See [Advanced serialization for `child_process`][] for more details.

### Event: `'multipleResolves'`

<!-- YAML
added: v10.12.0
deprecated:
- v17.6.0
- v16.15.0
-->

> Stability: 0 - Deprecated

* `type` {string} The resolution type. One of `'resolve'` or `'reject'`.
* `promise` {Promise} The promise that resolved or rejected more than once.
* `value` {any} The value with which the promise was either resolved or
rejected after the original resolve.

The `'multipleResolves'` event is emitted whenever a `Promise` has been either:

* Resolved more than once.
* Rejected more than once.
* Rejected after resolve.
* Resolved after reject.

This is useful for tracking potential errors in an application while using the
`Promise` constructor, as multiple resolutions are silently swallowed. However,
the occurrence of this event does not necessarily indicate an error. For
example, [`Promise.race()`][] can trigger a `'multipleResolves'` event.

Because of the unreliability of the event in cases like the
[`Promise.race()`][] example above it has been deprecated.

```mjs
import process from 'node:process';

process.on('multipleResolves', (type, promise, reason) => {
console.error(type, promise, reason);
setImmediate(() => process.exit(1));
});

async function main() {
try {
return await new Promise((resolve, reject) => {
resolve('First call');
resolve('Swallowed resolve');
reject(new Error('Swallowed reject'));
});
} catch {
throw new Error('Failed');
}
}

main().then(console.log);
// resolve: Promise { 'First call' } 'Swallowed resolve'
// reject: Promise { 'First call' } Error: Swallowed reject
// at Promise (*)
// at new Promise (<anonymous>)
// at main (*)
// First call
```

```cjs
const process = require('node:process');

process.on('multipleResolves', (type, promise, reason) => {
console.error(type, promise, reason);
setImmediate(() => process.exit(1));
});

async function main() {
try {
return await new Promise((resolve, reject) => {
resolve('First call');
resolve('Swallowed resolve');
reject(new Error('Swallowed reject'));
});
} catch {
throw new Error('Failed');
}
}

main().then(console.log);
// resolve: Promise { 'First call' } 'Swallowed resolve'
// reject: Promise { 'First call' } Error: Swallowed reject
// at Promise (*)
// at new Promise (<anonymous>)
// at main (*)
// First call
```

### Event: `'rejectionHandled'`

<!-- YAML
Expand Down Expand Up @@ -4603,7 +4514,6 @@ cases:
[`Error`]: errors.md#class-error
[`EventEmitter`]: events.md#class-eventemitter
[`NODE_OPTIONS`]: cli.md#node_optionsoptions
[`Promise.race()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
[`Worker`]: worker_threads.md#class-worker
[`Worker` constructor]: worker_threads.md#new-workerfilename-options
[`console.error()`]: console.md#consoleerrordata-args
Expand Down
70 changes: 4 additions & 66 deletions lib/internal/process/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ const {
setPromiseRejectCallback,
} = internalBinding('task_queue');

const { deprecate } = require('internal/util');

const {
noSideEffectsToString,
triggerUncaughtException,
Expand All @@ -43,27 +41,6 @@ const AsyncContextFrame = require('internal/async_context_frame');
// *Must* match Environment::TickInfo::Fields in src/env.h.
const kHasRejectionToWarn = 1;

// By default true because in cases where process is not a global
// it is not possible to determine if the user has added a listener
// to the process object.
let hasMultipleResolvesListener = true;

if (process.on) {
hasMultipleResolvesListener = process.listenerCount('multipleResolves') !== 0;

process.on('newListener', (eventName) => {
if (eventName === 'multipleResolves') {
hasMultipleResolvesListener = true;
}
});

process.on('removeListener', (eventName) => {
if (eventName === 'multipleResolves') {
hasMultipleResolvesListener = process.listenerCount('multipleResolves') !== 0;
}
});
}

/**
* Errors & Warnings
*/
Expand Down Expand Up @@ -192,55 +169,16 @@ function promiseRejectHandler(type, promise, reason) {
handledRejection(promise);
break;
case kPromiseRejectAfterResolved: // 2
if (hasMultipleResolvesListener) {
resolveErrorReject(promise, reason);
}
// Do nothing in this case. Previous we would emit a multipleResolves
// event but that was deprecated then later removed.
break;
case kPromiseResolveAfterResolved: // 3
if (hasMultipleResolvesListener) {
resolveErrorResolve(promise, reason);
}
// Do nothing in this case. Previous we would emit a multipleResolves
// event but that was deprecated then later removed.
break;
}
}

const multipleResolvesDeprecate = deprecate(
() => {},
'The multipleResolves event has been deprecated.',
'DEP0160',
);

/**
* @param {Promise} promise
* @param {Error} reason
*/
function resolveErrorResolve(promise, reason) {
// We have to wrap this in a next tick. Otherwise the error could be caught by
// the executed promise.
process.nextTick(() => {
// Emit the multipleResolves event.
// This is a deprecated event, so we have to check if it's being listened to.
if (process.emit('multipleResolves', 'resolve', promise, reason)) {
// If the event is being listened to, emit a deprecation warning.
multipleResolvesDeprecate();
}
});
}

/**
* @param {Promise} promise
* @param {Error} reason
*/
function resolveErrorReject(promise, reason) {
// We have to wrap this in a next tick. Otherwise the error could be caught by
// the executed promise.
process.nextTick(() => {
if (process.emit('multipleResolves', 'reject', promise, reason)) {
multipleResolvesDeprecate();
}
});
}

/**
* @param {Promise} promise
* @param {PromiseInfo} promiseInfo
Expand Down
1 change: 0 additions & 1 deletion test/parallel/test-events-once.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ async function stopListeningAfterCatchingError() {
} catch (_e) {
err = _e;
}
process.removeAllListeners('multipleResolves');
strictEqual(err, expected);
strictEqual(ee.listenerCount('error'), 0);
strictEqual(ee.listenerCount('myevent'), 0);
Expand Down
58 changes: 0 additions & 58 deletions test/parallel/test-promise-swallowed-event.js

This file was deleted.

2 changes: 0 additions & 2 deletions test/parallel/test-timers-immediate-promisified.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const setPromiseImmediate = promisify(timers.setImmediate);

assert.strictEqual(setPromiseImmediate, timerPromises.setImmediate);

process.on('multipleResolves', common.mustNotCall());

{
const promise = setPromiseImmediate();
promise.then(common.mustCall((value) => {
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-timers-interval-promisified.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const setPromiseTimeout = promisify(timers.setTimeout);

const { setInterval } = timerPromises;

process.on('multipleResolves', common.mustNotCall());

{
const iterable = setInterval(1, undefined);
const iterator = iterable[Symbol.asyncIterator]();
Expand Down
2 changes: 0 additions & 2 deletions test/parallel/test-timers-timeout-promisified.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const setPromiseTimeout = promisify(timers.setTimeout);

assert.strictEqual(setPromiseTimeout, timerPromises.setTimeout);

process.on('multipleResolves', common.mustNotCall());

{
const promise = setPromiseTimeout(1);
promise.then(common.mustCall((value) => {
Expand Down
14 changes: 0 additions & 14 deletions test/parallel/test-warn-multipleResolves.mjs

This file was deleted.

Loading