Skip to content

Commit 580ca38

Browse files
promise: Add --throw-unhandled-rejection and --trace-unhandled-rejection option
--throw-unhandled-rejection option can throw Exception when no unhandledRejection listener --trace-unhandled-rejection option can output error on stderr when no unhandledRejection listener
1 parent 1e4d053 commit 580ca38

File tree

6 files changed

+149
-0
lines changed

6 files changed

+149
-0
lines changed

doc/api/process.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ this, you can either attach a dummy `.catch(() => { })` handler to
186186
`resource.loaded`, preventing the `'unhandledRejection'` event from being
187187
emitted, or you can use the [`'rejectionHandled'`][] event.
188188

189+
When `'--throw-unhandled-rejection'` option is on, Node process throws
190+
an exception if `unhandledRejection` listener is not exist.
191+
192+
And `'--trace-unhandled-rejection'` option is on, Node outputs stderr message
193+
an exception if `unhandledRejection` listener is not exist.
194+
189195
## Event: 'warning'
190196

191197
Emitted whenever Node.js emits a process warning.

lib/internal/process/promises.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
const promiseRejectEvent = process._promiseRejectEvent;
44
const hasBeenNotifiedProperty = new WeakMap();
55
const pendingUnhandledRejections = [];
6+
const traceUnhandledRejection = process.traceUnhandledRejection;
7+
const throwUnhandledRejection = process.throwUnhandledRejection;
8+
const prefix = `(${process.release.name}:${process.pid}) `;
69

710
exports.setup = setupPromises;
811

@@ -44,6 +47,18 @@ function setupPromises(scheduleMicrotasks) {
4447
if (!process.emit('unhandledRejection', reason, promise)) {
4548
// Nobody is listening.
4649
// TODO(petkaantonov) Take some default action, see #830
50+
51+
if (traceUnhandledRejection) {
52+
if (reason && reason.stack) {
53+
console.error(`${prefix}${reason.stack}`);
54+
} else {
55+
console.error(`${prefix}${reason}`);
56+
}
57+
}
58+
59+
if (throwUnhandledRejection) {
60+
throw reason;
61+
}
4762
} else {
4863
hadListeners = true;
4964
}

src/node.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ static bool trace_deprecation = false;
143143
static bool throw_deprecation = false;
144144
static bool trace_sync_io = false;
145145
static bool track_heap_objects = false;
146+
static bool trace_unhandled_rejection = false;
147+
static bool throw_unhandled_rejection = false;
146148
static const char* eval_string = nullptr;
147149
static unsigned int preload_module_count = 0;
148150
static const char** preload_modules = nullptr;
@@ -3142,6 +3144,18 @@ void SetupProcessObject(Environment* env,
31423144
READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate()));
31433145
}
31443146

3147+
// --trace-unhandled-rejection
3148+
if (trace_unhandled_rejection) {
3149+
READONLY_PROPERTY(
3150+
process, "traceUnhandledRejection", True(env->isolate()));
3151+
}
3152+
3153+
// --throw-unhandled-rejection
3154+
if (throw_unhandled_rejection) {
3155+
READONLY_PROPERTY(
3156+
process, "throwUnhandledRejection", True(env->isolate()));
3157+
}
3158+
31453159
// --security-revert flags
31463160
#define V(code, _, __) \
31473161
do { \
@@ -3571,6 +3585,10 @@ static void ParseArgs(int* argc,
35713585
} else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
35723586
icu_data_dir = arg + 15;
35733587
#endif
3588+
} else if (strcmp(arg, "--trace-unhandled-rejection") == 0) {
3589+
trace_unhandled_rejection = true;
3590+
} else if (strcmp(arg, "--throw-unhandled-rejection") == 0) {
3591+
throw_unhandled_rejection = true;
35743592
} else if (strcmp(arg, "--expose-internals") == 0 ||
35753593
strcmp(arg, "--expose_internals") == 0) {
35763594
// consumed in js
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const spawn = require('child_process').spawn;
6+
const node = process.execPath;
7+
8+
if (process.argv[2] === 'child') {
9+
const rejectPromise = () => {
10+
return new Promise((resolve, reject) => {
11+
setTimeout(() => reject(new Error('Reject Promise')), 100);
12+
});
13+
};
14+
rejectPromise();
15+
} else {
16+
run('--throw-unhandled-rejection');
17+
}
18+
19+
function run(flags) {
20+
const args = [__filename, 'child'];
21+
if (flags)
22+
args.unshift(flags);
23+
24+
const child = spawn(node, args);
25+
let message = '';
26+
child.stderr.on('data', (data) => {
27+
message += data;
28+
});
29+
child.stderr.on('end', common.mustCall(() => {
30+
assert(message.match(/Reject Promise/));
31+
}));
32+
child.on('exit', common.mustCall((code) => {
33+
assert.strictEqual(code, 1);
34+
}));
35+
}
36+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const spawn = require('child_process').spawn;
6+
const node = process.execPath;
7+
8+
if (process.argv[2] === 'child') {
9+
const rejectPromise = () => {
10+
return new Promise((resolve, reject) => {
11+
setTimeout(() => reject(new Error('Reject Promise')), 100);
12+
});
13+
};
14+
rejectPromise();
15+
} else {
16+
run('--trace-unhandled-rejection');
17+
}
18+
19+
function run(flags) {
20+
const args = [__filename, 'child'];
21+
if (flags)
22+
args.unshift(flags);
23+
24+
const child = spawn(node, args);
25+
let errorMessage = '';
26+
child.stderr.on('data', (data) => {
27+
errorMessage += data;
28+
});
29+
child.stderr.on('end', common.mustCall(() => {
30+
assert(errorMessage.match(/Reject Promise/));
31+
}));
32+
child.on('exit', common.mustCall((code) => {
33+
assert.strictEqual(code, 0);
34+
}));
35+
}
36+
37+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const spawn = require('child_process').spawn;
6+
const node = process.execPath;
7+
8+
if (process.argv[2] === 'child') {
9+
const rejectPromise = () => {
10+
return new Promise((resolve, reject) => {
11+
setTimeout(() => reject(null), 100);
12+
});
13+
};
14+
rejectPromise();
15+
} else {
16+
run('--trace-unhandled-rejection');
17+
}
18+
19+
function run(flags) {
20+
const args = [__filename, 'child'];
21+
if (flags)
22+
args.unshift(flags);
23+
24+
const child = spawn(node, args);
25+
let message = '';
26+
child.stderr.on('data', (data) => {
27+
message += data;
28+
});
29+
child.stderr.on('end', common.mustCall(() => {
30+
assert(message.match(/null/));
31+
}));
32+
child.on('exit', common.mustCall((code) => {
33+
assert.strictEqual(code, 0);
34+
}));
35+
}
36+
37+

0 commit comments

Comments
 (0)