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
|
<!-- YAML
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/58707
|
||||||
|
description: End-of-Life.
|
||||||
- version: v18.0.0
|
- version: v18.0.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/41896
|
pr-url: https://github.com/nodejs/node/pull/41896
|
||||||
description: Runtime deprecation.
|
description: Runtime deprecation.
|
||||||
|
|
@ -3344,10 +3347,10 @@ changes:
|
||||||
description: Documentation-only deprecation.
|
description: Documentation-only deprecation.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Type: Runtime
|
Type: End-of-Life
|
||||||
|
|
||||||
This event was deprecated because it did not work with V8 promise combinators
|
This event was deprecated and removed because it did not work with V8 promise
|
||||||
which diminished its usefulness.
|
combinators which diminished its usefulness.
|
||||||
|
|
||||||
### DEP0161: `process._getActiveRequests()` and `process._getActiveHandles()`
|
### DEP0161: `process._getActiveRequests()` and `process._getActiveHandles()`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,95 +176,6 @@ process, the `message` argument can contain data that JSON is not able
|
||||||
to represent.
|
to represent.
|
||||||
See [Advanced serialization for `child_process`][] for more details.
|
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'`
|
### Event: `'rejectionHandled'`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
|
@ -4603,7 +4514,6 @@ cases:
|
||||||
[`Error`]: errors.md#class-error
|
[`Error`]: errors.md#class-error
|
||||||
[`EventEmitter`]: events.md#class-eventemitter
|
[`EventEmitter`]: events.md#class-eventemitter
|
||||||
[`NODE_OPTIONS`]: cli.md#node_optionsoptions
|
[`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`]: worker_threads.md#class-worker
|
||||||
[`Worker` constructor]: worker_threads.md#new-workerfilename-options
|
[`Worker` constructor]: worker_threads.md#new-workerfilename-options
|
||||||
[`console.error()`]: console.md#consoleerrordata-args
|
[`console.error()`]: console.md#consoleerrordata-args
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@ const {
|
||||||
setPromiseRejectCallback,
|
setPromiseRejectCallback,
|
||||||
} = internalBinding('task_queue');
|
} = internalBinding('task_queue');
|
||||||
|
|
||||||
const { deprecate } = require('internal/util');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
noSideEffectsToString,
|
noSideEffectsToString,
|
||||||
triggerUncaughtException,
|
triggerUncaughtException,
|
||||||
|
|
@ -43,27 +41,6 @@ const AsyncContextFrame = require('internal/async_context_frame');
|
||||||
// *Must* match Environment::TickInfo::Fields in src/env.h.
|
// *Must* match Environment::TickInfo::Fields in src/env.h.
|
||||||
const kHasRejectionToWarn = 1;
|
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
|
* Errors & Warnings
|
||||||
*/
|
*/
|
||||||
|
|
@ -192,55 +169,16 @@ function promiseRejectHandler(type, promise, reason) {
|
||||||
handledRejection(promise);
|
handledRejection(promise);
|
||||||
break;
|
break;
|
||||||
case kPromiseRejectAfterResolved: // 2
|
case kPromiseRejectAfterResolved: // 2
|
||||||
if (hasMultipleResolvesListener) {
|
// Do nothing in this case. Previous we would emit a multipleResolves
|
||||||
resolveErrorReject(promise, reason);
|
// event but that was deprecated then later removed.
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case kPromiseResolveAfterResolved: // 3
|
case kPromiseResolveAfterResolved: // 3
|
||||||
if (hasMultipleResolvesListener) {
|
// Do nothing in this case. Previous we would emit a multipleResolves
|
||||||
resolveErrorResolve(promise, reason);
|
// event but that was deprecated then later removed.
|
||||||
}
|
|
||||||
break;
|
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 {Promise} promise
|
||||||
* @param {PromiseInfo} promiseInfo
|
* @param {PromiseInfo} promiseInfo
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,6 @@ async function stopListeningAfterCatchingError() {
|
||||||
} catch (_e) {
|
} catch (_e) {
|
||||||
err = _e;
|
err = _e;
|
||||||
}
|
}
|
||||||
process.removeAllListeners('multipleResolves');
|
|
||||||
strictEqual(err, expected);
|
strictEqual(err, expected);
|
||||||
strictEqual(ee.listenerCount('error'), 0);
|
strictEqual(ee.listenerCount('error'), 0);
|
||||||
strictEqual(ee.listenerCount('myevent'), 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);
|
assert.strictEqual(setPromiseImmediate, timerPromises.setImmediate);
|
||||||
|
|
||||||
process.on('multipleResolves', common.mustNotCall());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const promise = setPromiseImmediate();
|
const promise = setPromiseImmediate();
|
||||||
promise.then(common.mustCall((value) => {
|
promise.then(common.mustCall((value) => {
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ const setPromiseTimeout = promisify(timers.setTimeout);
|
||||||
|
|
||||||
const { setInterval } = timerPromises;
|
const { setInterval } = timerPromises;
|
||||||
|
|
||||||
process.on('multipleResolves', common.mustNotCall());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const iterable = setInterval(1, undefined);
|
const iterable = setInterval(1, undefined);
|
||||||
const iterator = iterable[Symbol.asyncIterator]();
|
const iterator = iterable[Symbol.asyncIterator]();
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ const setPromiseTimeout = promisify(timers.setTimeout);
|
||||||
|
|
||||||
assert.strictEqual(setPromiseTimeout, timerPromises.setTimeout);
|
assert.strictEqual(setPromiseTimeout, timerPromises.setTimeout);
|
||||||
|
|
||||||
process.on('multipleResolves', common.mustNotCall());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
const promise = setPromiseTimeout(1);
|
const promise = setPromiseTimeout(1);
|
||||||
promise.then(common.mustCall((value) => {
|
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