Skip to content

Commit

Permalink
promise: Add --throw-unhandled-rejection and --trace-unhandled-reject…
Browse files Browse the repository at this point in the history
…ion 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
  • Loading branch information
yosuke-furukawa committed Apr 24, 2016
1 parent 1e4d053 commit 580ca38
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 0 deletions.
6 changes: 6 additions & 0 deletions doc/api/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ this, you can either attach a dummy `.catch(() => { })` handler to
`resource.loaded`, preventing the `'unhandledRejection'` event from being
emitted, or you can use the [`'rejectionHandled'`][] event.

When `'--throw-unhandled-rejection'` option is on, Node process throws
an exception if `unhandledRejection` listener is not exist.

And `'--trace-unhandled-rejection'` option is on, Node outputs stderr message
an exception if `unhandledRejection` listener is not exist.

## Event: 'warning'

Emitted whenever Node.js emits a process warning.
Expand Down
15 changes: 15 additions & 0 deletions lib/internal/process/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const promiseRejectEvent = process._promiseRejectEvent;
const hasBeenNotifiedProperty = new WeakMap();
const pendingUnhandledRejections = [];
const traceUnhandledRejection = process.traceUnhandledRejection;
const throwUnhandledRejection = process.throwUnhandledRejection;
const prefix = `(${process.release.name}:${process.pid}) `;

exports.setup = setupPromises;

Expand Down Expand Up @@ -44,6 +47,18 @@ function setupPromises(scheduleMicrotasks) {
if (!process.emit('unhandledRejection', reason, promise)) {
// Nobody is listening.
// TODO(petkaantonov) Take some default action, see #830

if (traceUnhandledRejection) {
if (reason && reason.stack) {
console.error(`${prefix}${reason.stack}`);
} else {
console.error(`${prefix}${reason}`);
}
}

if (throwUnhandledRejection) {
throw reason;
}
} else {
hadListeners = true;
}
Expand Down
18 changes: 18 additions & 0 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ static bool trace_deprecation = false;
static bool throw_deprecation = false;
static bool trace_sync_io = false;
static bool track_heap_objects = false;
static bool trace_unhandled_rejection = false;
static bool throw_unhandled_rejection = false;
static const char* eval_string = nullptr;
static unsigned int preload_module_count = 0;
static const char** preload_modules = nullptr;
Expand Down Expand Up @@ -3142,6 +3144,18 @@ void SetupProcessObject(Environment* env,
READONLY_PROPERTY(process, "traceDeprecation", True(env->isolate()));
}

// --trace-unhandled-rejection
if (trace_unhandled_rejection) {
READONLY_PROPERTY(
process, "traceUnhandledRejection", True(env->isolate()));
}

// --throw-unhandled-rejection
if (throw_unhandled_rejection) {
READONLY_PROPERTY(
process, "throwUnhandledRejection", True(env->isolate()));
}

// --security-revert flags
#define V(code, _, __) \
do { \
Expand Down Expand Up @@ -3571,6 +3585,10 @@ static void ParseArgs(int* argc,
} else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
icu_data_dir = arg + 15;
#endif
} else if (strcmp(arg, "--trace-unhandled-rejection") == 0) {
trace_unhandled_rejection = true;
} else if (strcmp(arg, "--throw-unhandled-rejection") == 0) {
throw_unhandled_rejection = true;
} else if (strcmp(arg, "--expose-internals") == 0 ||
strcmp(arg, "--expose_internals") == 0) {
// consumed in js
Expand Down
36 changes: 36 additions & 0 deletions test/parallel/test-throw-default-error-unhandled-rejections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const spawn = require('child_process').spawn;
const node = process.execPath;

if (process.argv[2] === 'child') {
const rejectPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Reject Promise')), 100);
});
};
rejectPromise();
} else {
run('--throw-unhandled-rejection');
}

function run(flags) {
const args = [__filename, 'child'];
if (flags)
args.unshift(flags);

const child = spawn(node, args);
let message = '';
child.stderr.on('data', (data) => {
message += data;
});
child.stderr.on('end', common.mustCall(() => {
assert(message.match(/Reject Promise/));
}));
child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 1);
}));
}

37 changes: 37 additions & 0 deletions test/parallel/test-trace-default-error-unhandled-rejections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const spawn = require('child_process').spawn;
const node = process.execPath;

if (process.argv[2] === 'child') {
const rejectPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Reject Promise')), 100);
});
};
rejectPromise();
} else {
run('--trace-unhandled-rejection');
}

function run(flags) {
const args = [__filename, 'child'];
if (flags)
args.unshift(flags);

const child = spawn(node, args);
let errorMessage = '';
child.stderr.on('data', (data) => {
errorMessage += data;
});
child.stderr.on('end', common.mustCall(() => {
assert(errorMessage.match(/Reject Promise/));
}));
child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 0);
}));
}


37 changes: 37 additions & 0 deletions test/parallel/test-trace-null-unhandled-rejections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const spawn = require('child_process').spawn;
const node = process.execPath;

if (process.argv[2] === 'child') {
const rejectPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => reject(null), 100);
});
};
rejectPromise();
} else {
run('--trace-unhandled-rejection');
}

function run(flags) {
const args = [__filename, 'child'];
if (flags)
args.unshift(flags);

const child = spawn(node, args);
let message = '';
child.stderr.on('data', (data) => {
message += data;
});
child.stderr.on('end', common.mustCall(() => {
assert(message.match(/null/));
}));
child.on('exit', common.mustCall((code) => {
assert.strictEqual(code, 0);
}));
}


0 comments on commit 580ca38

Please sign in to comment.