mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
process: move multipleResolves event to EOL
The `multipleResolves` event has been deprecated for several years now. It's time. PR-URL: https://github.com/nodejs/node/pull/58707 Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
parent
4d5ee2491b
commit
a3dfca90d1
|
|
@ -3334,6 +3334,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.
|
||||
|
|
@ -3344,10 +3347,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()`
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ const {
|
|||
setPromiseRejectCallback,
|
||||
} = internalBinding('task_queue');
|
||||
|
||||
const { deprecate } = require('internal/util');
|
||||
|
||||
const {
|
||||
noSideEffectsToString,
|
||||
triggerUncaughtException,
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const rejection = new Error('Swallowed reject');
|
||||
const rejection2 = new TypeError('Weird');
|
||||
const resolveMessage = 'First call';
|
||||
const rejectPromise = new Promise((r) => setTimeout(r, 10, rejection2));
|
||||
const swallowedResolve = 'Swallowed resolve';
|
||||
const swallowedResolve2 = 'Foobar';
|
||||
|
||||
process.on('multipleResolves', common.mustCall(handler, 4));
|
||||
|
||||
const p1 = new Promise((resolve, reject) => {
|
||||
resolve(resolveMessage);
|
||||
resolve(swallowedResolve);
|
||||
reject(rejection);
|
||||
});
|
||||
|
||||
const p2 = new Promise((resolve, reject) => {
|
||||
reject(rejectPromise);
|
||||
resolve(swallowedResolve2);
|
||||
reject(rejection2);
|
||||
}).catch(common.mustCall((exception) => {
|
||||
assert.strictEqual(exception, rejectPromise);
|
||||
}));
|
||||
|
||||
const expected = [
|
||||
'resolve',
|
||||
p1,
|
||||
swallowedResolve,
|
||||
'reject',
|
||||
p1,
|
||||
rejection,
|
||||
'resolve',
|
||||
p2,
|
||||
swallowedResolve2,
|
||||
'reject',
|
||||
p2,
|
||||
rejection2,
|
||||
];
|
||||
|
||||
let count = 0;
|
||||
|
||||
function handler(type, promise, reason) {
|
||||
assert.strictEqual(type, expected.shift());
|
||||
// In the first two cases the promise is identical because it's not delayed.
|
||||
// The other two cases are not identical, because the `promise` is caught in a
|
||||
// state when it has no knowledge about the `.catch()` handler that is
|
||||
// attached to it right afterwards.
|
||||
if (count++ < 2) {
|
||||
assert.strictEqual(promise, expected.shift());
|
||||
} else {
|
||||
assert.notStrictEqual(promise, expected.shift());
|
||||
}
|
||||
assert.strictEqual(reason, expected.shift());
|
||||
}
|
||||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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]();
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import { expectWarning, mustCall } from '../common/index.mjs';
|
||||
|
||||
expectWarning(
|
||||
'DeprecationWarning',
|
||||
'The multipleResolves event has been deprecated.',
|
||||
'DEP0160',
|
||||
);
|
||||
|
||||
process.on('multipleResolves', mustCall());
|
||||
|
||||
new Promise((resolve) => {
|
||||
resolve();
|
||||
resolve();
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user