diff --git a/CHANGELOG.md b/CHANGELOG.md index 28e430441bb4..2586c942eddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `.forEach` method to iterable DOM collections ([#329](https://github.com/zloirock/core-js/issues/329)) - `Symbol#description` ([stage 1 proposal](https://tc39.github.io/proposal-Symbol-description/)) - `String#replaceAll` ([stage 1 proposal](https://github.com/psmarshall/string-replace-all-proposal)) + - Triggering unhandled `Promise` rejection events (instead of only global handlers) [#205](https://github.com/zloirock/core-js/issues/205) - Removed obsolete features: - `Error.isError` (withdrawn) - `System.global` (replaced by `global`) diff --git a/README.md b/README.md index c0a339a8b90e..6d2fbf5a1d09 100644 --- a/README.md +++ b/README.md @@ -801,6 +801,9 @@ setTimeout(() => promise.catch(() => {}), 1e3); ``` In a browser on rejection, by default, you will see notify in the console, or you can add a custom handler and a handler on handling unhandled, [*example*](http://goo.gl/Wozskl): ```js +window.addEventListener('unhandledrejection', e => console.log('unhandled', e.reason, e.promise)); +window.addEventListener('rejectionhandled', e => console.log('handled', e.reason, e.promise)); +// or window.onunhandledrejection = e => console.log('unhandled', e.reason, e.promise); window.onrejectionhandled = e => console.log('handled', e.reason, e.promise); diff --git a/modules/es.promise.js b/modules/es.promise.js index 6a1fdefe29c9..54ba5cc5f5a5 100644 --- a/modules/es.promise.js +++ b/modules/es.promise.js @@ -18,11 +18,15 @@ var hostReportErrors = require('./_host-report-errors'); var PROMISE = 'Promise'; var TypeError = global.TypeError; var process = global.process; +var document = global.document; var $Promise = global[PROMISE]; var isNode = classof(process) == 'process'; var empty = function () { /* empty */ }; var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper; var newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f; +var DISPATCH_EVENT = !!(document && document.createEvent && global.dispatchEvent); +var UNHANDLED_REJECTION = 'unhandledrejection'; +var REJECTION_HANDLED = 'rejectionhandled'; var USE_NATIVE = !!function () { try { @@ -87,18 +91,28 @@ var notify = function (promise, isReject) { if (isReject && !promise._h) onUnhandled(promise); }); }; +var dispatchEvent = function (name, promise, reason) { + var event, handler; + if (DISPATCH_EVENT) { + event = document.createEvent('Event'); + event.promise = promise; + event.reason = reason; + event.initEvent(name, false, true); + global.dispatchEvent(event); + } else event = { promise: promise, reason: reason }; + if (handler = global['on' + name]) handler(event); + else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason); +}; var onUnhandled = function (promise) { task.call(global, function () { var value = promise._v; var unhandled = isUnhandled(promise); - var result, handler; + var result; if (unhandled) { result = perform(function () { if (isNode) { process.emit('unhandledRejection', value, promise); - } else if (handler = global.onunhandledrejection) { - handler({ promise: promise, reason: value }); - } else hostReportErrors('Unhandled promise rejection', value); + } else dispatchEvent(UNHANDLED_REJECTION, promise, value); }); // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should promise._h = isNode || isUnhandled(promise) ? 2 : 1; @@ -111,12 +125,9 @@ var isUnhandled = function (promise) { }; var onHandleUnhandled = function (promise) { task.call(global, function () { - var handler; if (isNode) { process.emit('rejectionHandled', promise); - } else if (handler = global.onrejectionhandled) { - handler({ promise: promise, reason: promise._v }); - } + } else dispatchEvent(REJECTION_HANDLED, promise, promise._v); }); }; var $reject = function (value) { diff --git a/tests/library/es.promise.js b/tests/library/es.promise.js index 9ba3d42332bb..0841dc35cd1d 100644 --- a/tests/library/es.promise.js +++ b/tests/library/es.promise.js @@ -390,7 +390,21 @@ QUnit.test('Unhandled rejection tracking', assert => { process.on('unhandledRejection', onunhandledrejection); process.on('rejectionHandled', onrejectionhandled); } else { - assert.expect(4); + if (GLOBAL.addEventListener) { + assert.expect(8); + function onunhandledrejection(it) { + assert.same(it.promise, $promise, 'addEventListener(unhandledrejection), promise'); + assert.same(it.reason, 42, 'addEventListener(unhandledrejection), reason'); + GLOBAL.removeEventListener('unhandledrejection', onunhandledrejection); + } + GLOBAL.addEventListener('rejectionhandled', onunhandledrejection); + function onrejectionhandled(it) { + assert.same(it.promise, $promise, 'addEventListener(rejectionhandled), promise'); + assert.same(it.reason, 42, 'addEventListener(rejectionhandled), reason'); + GLOBAL.removeEventListener('rejectionhandled', onrejectionhandled); + } + GLOBAL.addEventListener('rejectionhandled', onrejectionhandled); + } else assert.expect(4); GLOBAL.onunhandledrejection = function (it) { assert.same(it.promise, $promise, 'onunhandledrejection, promise'); assert.same(it.reason, 42, 'onunhandledrejection, reason'); diff --git a/tests/tests/es.promise.js b/tests/tests/es.promise.js index 0e14b9b09b6c..bb3c2e2481f4 100644 --- a/tests/tests/es.promise.js +++ b/tests/tests/es.promise.js @@ -414,7 +414,21 @@ QUnit.test('Unhandled rejection tracking', assert => { process.on('unhandledRejection', onunhandledrejection); process.on('rejectionHandled', onrejectionhandled); } else { - assert.expect(4); + if (GLOBAL.addEventListener) { + assert.expect(8); + function onunhandledrejection(it) { + assert.same(it.promise, $promise, 'addEventListener(unhandledrejection), promise'); + assert.same(it.reason, 42, 'addEventListener(unhandledrejection), reason'); + GLOBAL.removeEventListener('unhandledrejection', onunhandledrejection); + } + GLOBAL.addEventListener('rejectionhandled', onunhandledrejection); + function onrejectionhandled(it) { + assert.same(it.promise, $promise, 'addEventListener(rejectionhandled), promise'); + assert.same(it.reason, 42, 'addEventListener(rejectionhandled), reason'); + GLOBAL.removeEventListener('rejectionhandled', onrejectionhandled); + } + GLOBAL.addEventListener('rejectionhandled', onrejectionhandled); + } else assert.expect(4); GLOBAL.onunhandledrejection = function (it) { assert.same(it.promise, $promise, 'onunhandledrejection, promise'); assert.same(it.reason, 42, 'onunhandledrejection, reason');