Skip to content

Commit 3b10f7f

Browse files
dfabulichmmarchini
authored andcommitted
process: change default --unhandled-rejections=throw
This is a semver-major change that resolves DEP0018. All users that have set an unhandledRejection hook or set a non-default value for the --unhandled-rejections flag will see no change in behavior after this change. Refs: https://nodejs.org/dist/latest/docs/api/deprecations.html#deprecations_dep0018_unhandled_promise_rejections PR-URL: #33021 Fixes: #20392 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Mary Marchini <[email protected]> Reviewed-By: Shelley Vohr <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Anatoli Papirovski <[email protected]> Reviewed-By: Сковорода Никита Андреевич <[email protected]>
1 parent 25d8456 commit 3b10f7f

15 files changed

+73
-97
lines changed

doc/api/cli.md

+1-5
Original file line numberDiff line numberDiff line change
@@ -1007,15 +1007,11 @@ added:
10071007
- v10.17.0
10081008
-->
10091009

1010-
By default all unhandled rejections trigger a warning plus a deprecation warning
1011-
for the very first unhandled rejection in case no [`unhandledRejection`][] hook
1012-
is used.
1013-
10141010
Using this flag allows to change what should happen when an unhandled rejection
10151011
occurs. One of the following modes can be chosen:
10161012

10171013
* `throw`: Emit [`unhandledRejection`][]. If this hook is not set, raise the
1018-
unhandled rejection as an uncaught exception.
1014+
unhandled rejection as an uncaught exception. This is the default.
10191015
* `strict`: Raise the unhandled rejection as an uncaught exception.
10201016
* `warn`: Always trigger a warning, no matter if the [`unhandledRejection`][]
10211017
hook is set or not but do not print the deprecation warning.

lib/internal/process/promises.js

+1-26
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ const kThrowUnhandledRejections = 3;
5757

5858
const kWarnWithErrorCodeUnhandledRejections = 4;
5959

60-
// --unhandled-rejections is unset:
61-
// Emit 'unhandledRejection', if it's unhandled, emit
62-
// 'UnhandledPromiseRejectionWarning', then emit deprecation warning.
63-
const kDefaultUnhandledRejections = 5;
64-
6560
let unhandledRejectionsMode;
6661

6762
function setHasRejectionToWarn(value) {
@@ -86,7 +81,7 @@ function getUnhandledRejectionsMode() {
8681
case 'warn-with-error-code':
8782
return kWarnWithErrorCodeUnhandledRejections;
8883
default:
89-
return kDefaultUnhandledRejections;
84+
return kThrowUnhandledRejections;
9085
}
9186
}
9287

@@ -175,15 +170,6 @@ function emitUnhandledRejectionWarning(uid, reason) {
175170
process.emitWarning(warning);
176171
}
177172

178-
let deprecationWarned = false;
179-
function emitDeprecationWarning() {
180-
process.emitWarning(
181-
'Unhandled promise rejections are deprecated. In the future, ' +
182-
'promise rejections that are not handled will terminate the ' +
183-
'Node.js process with a non-zero exit code.',
184-
'DeprecationWarning', 'DEP0018');
185-
}
186-
187173
// If this method returns true, we've executed user code or triggered
188174
// a warning to be emitted which requires the microtask and next tick
189175
// queues to be drained again.
@@ -241,17 +227,6 @@ function processPromiseRejections() {
241227
}
242228
break;
243229
}
244-
case kDefaultUnhandledRejections: {
245-
const handled = process.emit('unhandledRejection', reason, promise);
246-
if (!handled) {
247-
emitUnhandledRejectionWarning(uid, reason);
248-
if (!deprecationWarned) {
249-
emitDeprecationWarning();
250-
deprecationWarned = true;
251-
}
252-
}
253-
break;
254-
}
255230
}
256231
maybeScheduledTicksOrMicrotasks = true;
257232
}

test/common/index.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -599,11 +599,8 @@ function getBufferSources(buf) {
599599
return [...getArrayBufferViews(buf), new Uint8Array(buf).buffer];
600600
}
601601

602-
// Crash the process on unhandled rejections.
603-
const crashOnUnhandledRejection = (err) => { throw err; };
604-
process.on('unhandledRejection', crashOnUnhandledRejection);
605602
function disableCrashOnUnhandledRejection() {
606-
process.removeListener('unhandledRejection', crashOnUnhandledRejection);
603+
process.on('unhandledRejection', () => {});
607604
}
608605

609606
function getTTYfd() {

test/es-module/test-esm-cjs-load-error-note.mjs

+3-4
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ pImport2.stderr.on('data', (data) => {
6363
pImport2Stderr += data;
6464
});
6565
pImport2.on('close', mustCall((code) => {
66-
// As this exits normally we expect 0
67-
assert.strictEqual(code, 0);
66+
assert.strictEqual(code, expectedCode);
6867
assert.ok(!pImport2Stderr.includes(expectedNote),
6968
`${expectedNote} must not be included in ${pImport2Stderr}`);
7069
}));
@@ -94,15 +93,15 @@ pImport4.on('close', mustCall((code) => {
9493
`${expectedNote} not found in ${pImport4Stderr}`);
9594
}));
9695

97-
// Must exit with zero and show note
96+
// Must exit non-zero and show note
9897
const pImport5 = spawn(process.execPath, [Import5]);
9998
let pImport5Stderr = '';
10099
pImport5.stderr.setEncoding('utf8');
101100
pImport5.stderr.on('data', (data) => {
102101
pImport5Stderr += data;
103102
});
104103
pImport5.on('close', mustCall((code) => {
105-
assert.strictEqual(code, 0);
104+
assert.strictEqual(code, expectedCode);
106105
assert.ok(!pImport5Stderr.includes(expectedNote),
107106
`${expectedNote} must not be included in ${pImport5Stderr}`);
108107
}));
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
// Flags: --unhandled-rejections=warn-with-error-code
22
'use strict';
33

4-
const common = require('../common');
4+
require('../common');
55
const assert = require('assert');
66

7-
common.disableCrashOnUnhandledRejection();
8-
97
Promise.reject(new Error('alas'));
108
process.on('exit', assert.strictEqual.bind(null, 1));
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
// Flags: --trace-warnings
1+
// Flags: --trace-warnings --unhandled-rejections=warn
22
'use strict';
3-
const common = require('../common');
4-
common.disableCrashOnUnhandledRejection();
3+
require('../common');
54
const p = Promise.reject(new Error('This was rejected'));
65
setImmediate(() => p.catch(() => {}));

test/message/unhandled_promise_trace_warnings.out

-4
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717
at *
1818
at *
1919
at *
20-
(node:*) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
21-
at *
22-
at *
23-
at *
2420
(node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
2521
at handledRejection (internal/process/promises.js:*)
2622
at promiseRejectHandler (internal/process/promises.js:*)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const Countdown = require('../common/countdown');
5+
const assert = require('assert');
6+
7+
// Verify that unhandled rejections always trigger uncaught exceptions instead
8+
// of triggering unhandled rejections.
9+
10+
const err1 = new Error('One');
11+
const err2 = new Error(
12+
'This error originated either by throwing ' +
13+
'inside of an async function without a catch block, or by rejecting a ' +
14+
'promise which was not handled with .catch(). The promise rejected with the' +
15+
' reason "null".'
16+
);
17+
err2.code = 'ERR_UNHANDLED_REJECTION';
18+
Object.defineProperty(err2, 'name', {
19+
value: 'UnhandledPromiseRejection',
20+
writable: true,
21+
configurable: true
22+
});
23+
24+
const errors = [err1, err2];
25+
const identical = [true, false];
26+
27+
const ref = new Promise(() => {
28+
throw err1;
29+
});
30+
// Explicitly reject `null`.
31+
Promise.reject(null);
32+
33+
process.on('warning', common.mustNotCall('warning'));
34+
// If we add an unhandledRejection handler, the exception won't be thrown
35+
// process.on('unhandledRejection', common.mustCall(2));
36+
process.on('rejectionHandled', common.mustNotCall('rejectionHandled'));
37+
process.on('exit', assert.strictEqual.bind(null, 0));
38+
39+
const timer = setTimeout(() => console.log(ref), 1000);
40+
41+
const counter = new Countdown(2, () => {
42+
clearTimeout(timer);
43+
});
44+
45+
process.on('uncaughtException', common.mustCall((err, origin) => {
46+
counter.dec();
47+
assert.strictEqual(origin, 'unhandledRejection', err);
48+
const knownError = errors.shift();
49+
assert.deepStrictEqual(err, knownError);
50+
// Check if the errors are reference equal.
51+
assert(identical.shift() ? err === knownError : err !== knownError);
52+
}, 2));

test/parallel/test-promise-unhandled-throw.js

-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ const common = require('../common');
55
const Countdown = require('../common/countdown');
66
const assert = require('assert');
77

8-
common.disableCrashOnUnhandledRejection();
9-
108
// Verify that unhandled rejections always trigger uncaught exceptions instead
119
// of triggering unhandled rejections.
1210

test/parallel/test-promise-unhandled-warn-no-hook.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33

44
const common = require('../common');
55

6-
common.disableCrashOnUnhandledRejection();
7-
8-
// Verify that ignoring unhandled rejection works fine and that no warning is
9-
// logged.
6+
// Verify that --unhandled-rejections=warn works fine
107

118
new Promise(() => {
129
throw new Error('One');

test/parallel/test-promises-unhandled-proxy-rejections.js

+1-19
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,6 @@ const common = require('../common');
33

44
common.disableCrashOnUnhandledRejection();
55

6-
const expectedDeprecationWarning = ['Unhandled promise rejections are ' +
7-
'deprecated. In the future, promise ' +
8-
'rejections that are not handled will ' +
9-
'terminate the Node.js process with a ' +
10-
'non-zero exit code.', 'DEP0018'];
11-
const expectedPromiseWarning = ['Unhandled promise rejection. ' +
12-
'This error originated either by throwing ' +
13-
'inside of an async function without a catch ' +
14-
'block, or by rejecting a promise which was ' +
15-
'not handled with .catch(). To terminate the ' +
16-
'node process on unhandled promise rejection, ' +
17-
'use the CLI flag `--unhandled-rejections=strict` (see ' +
18-
'https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). ' +
19-
'(rejection id: 1)'];
20-
216
function throwErr() {
227
throw new Error('Error from proxy');
238
}
@@ -38,10 +23,7 @@ const thorny = new Proxy({}, {
3823
construct: throwErr
3924
});
4025

41-
common.expectWarning({
42-
DeprecationWarning: expectedDeprecationWarning,
43-
UnhandledPromiseRejectionWarning: expectedPromiseWarning,
44-
});
26+
process.on('warning', common.mustNotCall());
4527

4628
// Ensure this doesn't crash
4729
Promise.reject(thorny);

test/parallel/test-promises-unhandled-rejections.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ asyncTest('Throwing an error inside a rejectionHandled handler goes to' +
668668
' unhandledException, and does not cause .catch() to throw an ' +
669669
'exception', function(done) {
670670
clean();
671+
common.disableCrashOnUnhandledRejection();
671672
const e = new Error();
672673
const e2 = new Error();
673674
const tearDownException = setupException(function(err) {
@@ -702,19 +703,17 @@ asyncTest('Rejected promise inside unhandledRejection allows nextTick loop' +
702703
});
703704

704705
asyncTest(
705-
'Unhandled promise rejection emits a warning immediately',
706+
'Promise rejection triggers unhandledRejection immediately',
706707
function(done) {
707708
clean();
708709
Promise.reject(0);
709-
const { emitWarning } = process;
710-
process.emitWarning = common.mustCall((...args) => {
710+
process.on('unhandledRejection', common.mustCall((err) => {
711711
if (timer) {
712712
clearTimeout(timer);
713713
timer = null;
714714
done();
715715
}
716-
emitWarning(...args);
717-
}, 2);
716+
}));
718717

719718
let timer = setTimeout(common.mustNotCall(), 10000);
720719
},

test/parallel/test-promises-unhandled-symbol-rejections.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1+
// Flags: --unhandled-rejections=warn
12
'use strict';
23
const common = require('../common');
34

45
common.disableCrashOnUnhandledRejection();
56

67
const expectedValueWarning = ['Symbol()'];
7-
const expectedDeprecationWarning = ['Unhandled promise rejections are ' +
8-
'deprecated. In the future, promise ' +
9-
'rejections that are not handled will ' +
10-
'terminate the Node.js process with a ' +
11-
'non-zero exit code.', 'DEP0018'];
128
const expectedPromiseWarning = ['Unhandled promise rejection. ' +
139
'This error originated either by throwing ' +
1410
'inside of an async function without a catch ' +
@@ -20,7 +16,6 @@ const expectedPromiseWarning = ['Unhandled promise rejection. ' +
2016
'(rejection id: 1)'];
2117

2218
common.expectWarning({
23-
DeprecationWarning: expectedDeprecationWarning,
2419
UnhandledPromiseRejectionWarning: [
2520
expectedValueWarning,
2621
expectedPromiseWarning

test/parallel/test-promises-warning-on-unhandled-rejection.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --no-warnings
1+
// Flags: --no-warnings --unhandled-rejections=warn
22
'use strict';
33

44
// Test that warnings are emitted when a Promise experiences an uncaught
@@ -7,8 +7,6 @@
77
const common = require('../common');
88
const assert = require('assert');
99

10-
common.disableCrashOnUnhandledRejection();
11-
1210
let b = 0;
1311

1412
process.on('warning', common.mustCall((warning) => {
@@ -27,14 +25,10 @@ process.on('warning', common.mustCall((warning) => {
2725
);
2826
break;
2927
case 2:
30-
// One time deprecation warning, first unhandled rejection
31-
assert.strictEqual(warning.name, 'DeprecationWarning');
32-
break;
33-
case 3:
3428
// Number rejection error displayed. Note it's been stringified
3529
assert.strictEqual(warning.message, '42');
3630
break;
37-
case 4:
31+
case 3:
3832
// Unhandled rejection warning (won't be handled next tick)
3933
assert.strictEqual(warning.name, 'UnhandledPromiseRejectionWarning');
4034
assert(
@@ -43,13 +37,13 @@ process.on('warning', common.mustCall((warning) => {
4337
`but did not. Had "${warning.message}" instead.`
4438
);
4539
break;
46-
case 5:
40+
case 4:
4741
// Rejection handled asynchronously.
4842
assert.strictEqual(warning.name, 'PromiseRejectionHandledWarning');
4943
assert(/Promise rejection was handled asynchronously/
5044
.test(warning.message));
5145
}
52-
}, 6));
46+
}, 5));
5347

5448
const p = Promise.reject('This was rejected'); // Reject with a string
5549
setImmediate(common.mustCall(() => p.catch(() => { })));

test/sequential/test-inspector-async-call-stack-abort.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ const { strictEqual } = require('assert');
99
const eyecatcher = 'nou, houdoe he?';
1010

1111
if (process.argv[2] === 'child') {
12-
common.disableCrashOnUnhandledRejection();
1312
const { Session } = require('inspector');
1413
const { promisify } = require('util');
1514
const { internalBinding } = require('internal/test/binding');
@@ -31,7 +30,7 @@ if (process.argv[2] === 'child') {
3130
const options = { encoding: 'utf8' };
3231
const proc = spawnSync(
3332
process.execPath, ['--expose-internals', __filename, 'child'], options);
34-
strictEqual(proc.status, 0);
33+
strictEqual(proc.status, 1);
3534
strictEqual(proc.signal, null);
3635
strictEqual(proc.stderr.includes(eyecatcher), true);
3736
}

0 commit comments

Comments
 (0)