Skip to content

Commit

Permalink
Conditionally queue a task to notify
Browse files Browse the repository at this point in the history
This is option 5 in @bzbarsky's #2 (comment). Some test cases change since now we allow a bit more delay before attachment.
  • Loading branch information
domenic committed Jul 14, 2015
1 parent 8393af8 commit 2e069ec
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 49 deletions.
8 changes: 5 additions & 3 deletions README.md
Expand Up @@ -62,14 +62,15 @@ Environment settings object seems to be a place to dump stuff? Need to define th

- Outstanding rejected promises weak set
- About-to-be-notified rejected promises list
- Will notify about rejected promises flag

### [Perform a microtask checkpoint](https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint)

Insert a step between steps 8 and 9:

1. _Done:_ <a href="#user-content-notify-about-rejected-promises">Notify about rejected promises</a>.

Remove the "_Done:_" label from step 9.
1. If the about-to-be-notified rejected promises list is not empty, and the will notify about rejected promises flag is unset,
1. Queue a task to <a href="#user-content-notify-about-rejected-promises">notify about rejected promises</a>.
1. Set the will notify about rejected promises flag.

### Unhandled promise rejections

Expand Down Expand Up @@ -98,6 +99,7 @@ This implementation results in promise rejections being marked as **handled** or

To <a id="notify-about-rejected-promises">**notify about rejected promises**</a>, perform the following steps:

1. Unset the will notify about rejected promises flag.
1. For each entry _p_ in the about-to-be-notified rejected promises list,
1. Let _event_ be a new trusted `PromiseRejectionEvent` object that does not bubble and is cancelable, and which has the event name `unhandledrejection`.
1. Initialise _event_'s `promise` attribute to _p_.
Expand Down
179 changes: 133 additions & 46 deletions tests/resources/promise-rejection-events.js
Expand Up @@ -267,7 +267,7 @@ async_test(function(t) {
'a promise created from returning a Promise.reject-created promise in a fulfillment handler');

//
// Negative unhandledrejection/rejectionhandled tests with delayed attachment
// Negative unhandledrejection/rejectionhandled tests with microtask-delayed attachment
//

async_test(function(t) {
Expand Down Expand Up @@ -446,14 +446,14 @@ async_test(function(t) {
'all inside a setTimeout');

//
// Positive unhandledrejection/rejectionhandled tests with delayed attachment
// Negative unhandledrejection/rejectionhandled tests with task-delayed attachment
//

async_test(function(t) {
var e = new Error();
var p;

onUnhandledSucceed(t, e, function() { return p; });
onUnhandledFail(t, function() { return p; });

var _reject;
p = new Promise(function(_, reject) {
Expand All @@ -464,47 +464,87 @@ async_test(function(t) {
var unreached = t.unreached_func('promise should not be fulfilled');
p.then(unreached, function() {});
});
}, 'delayed handling: a task delay before attaching a handler does not prevent unhandledrejection');
}, 'delayed handling: a task delay before attaching a handler prevents unhandledrejection');

async_test(function(t) {
var unhandledPromises = [];
var unhandledReasons = [];
var e = new Error();
var p;

var unhandled = function(ev) {
if (ev.promise === p) {
t.step(function() {
unhandledPromises.push(ev.promise);
unhandledReasons.push(ev.reason);
onUnhandledFail(t, function() { return p; });

p = Promise.reject(e);
postMessageTask(function() {
Promise.resolve().then(function() {
p.catch(function() {});
});
});
}, 'delayed handling: postMessageTask after promise creation/rejection, plus promise microtasks, is not too late to ' +
'attach a rejection handler');

async_test(function(t) {
var e = new Error();
var p;

onUnhandledFail(t, function() { return p; });

postMessageTask(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
p.catch(function() {});
});
});
});
}
};
var handled = function(ev) {
if (ev.promise === p) {
t.step(function() {
assert_array_equals(unhandledPromises, [p]);
assert_array_equals(unhandledReasons, [e]);
assert_equals(ev.promise, p);
assert_equals(ev.reason, e);
});
});
p = Promise.reject(e);
}, 'delayed handling: postMessageTask before promise creation/rejection, plus many promise microtasks, is not too ' +
'late to attach a rejection handler');

async_test(function(t) {
var e = new Error();
var p;

onUnhandledFail(t, function() { return p; });

p = Promise.reject(e);
postMessageTask(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
p.catch(function() {});
});
});
});
}
};
addEventListener('unhandledrejection', unhandled);
addEventListener('rejectionhandled', handled);
ensureCleanup(t, unhandled, handled);
});
});
}, 'delayed handling: postMessageTask after promise creation/rejection, plus many promise microtasks, is not too ' +
'late to attach a rejection handler');

p = new Promise(function() {
throw e;
//
// Positive unhandledrejection/rejectionhandled tests with delayed attachment
//

async_test(function(t) {
var e = new Error();
var p;

onUnhandledSucceed(t, e, function() { return p; });

var _reject;
p = new Promise(function(_, reject) {
_reject = reject;
});
setTimeout(function() {
var unreached = t.unreached_func('promise should not be fulfilled');
p.then(unreached, function(reason) {
assert_equals(reason, e);
setTimeout(function() { t.done(); }, 10);
_reject(e);
postMessageTask(function() {
postMessageTask(function() {
var unreached = t.unreached_func('promise should not be fulfilled');
p.then(unreached, function() {});
});
}, 10);
}, 'delayed handling: delaying handling by setTimeout(,10) will cause both events to fire');
});
}, 'delayed handling: a nested-task delay before attaching a handler causes unhandledrejection');

async_test(function(t) {
var e = new Error();
Expand All @@ -514,32 +554,37 @@ async_test(function(t) {

p = Promise.reject(e);
postMessageTask(function() {
Promise.resolve().then(function() {
p.catch(function() {});
postMessageTask(function() {
Promise.resolve().then(function() {
p.catch(function() {});
});
});
});
}, 'delayed handling: postMessageTask after promise creation/rejection, plus promise microtasks, is too late to ' +
'attach a rejection handler');
}, 'delayed handling: a nested-postMessageTask after promise creation/rejection, plus promise microtasks, is too ' +
'late to attach a rejection handler');

async_test(function(t) {
var e = new Error();
var p;

onUnhandledSucceed(t, e, function() { return p; });

postMessageTask(function() {
Promise.resolve().then(function() {
postMessageTask(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
p.catch(function() {});
Promise.resolve().then(function() {
p.catch(function() {});
});
});
});
});
});
});
p = Promise.reject(e);
}, 'delayed handling: postMessageTask before promise creation/rejection, plus many promise microtasks, is too late ' +
'to attach a rejection handler');
}, 'delayed handling: a nested-postMessageTask before promise creation/rejection, plus many promise microtasks, is ' +
'too late to attach a rejection handler');

async_test(function(t) {
var e = new Error();
Expand All @@ -549,18 +594,60 @@ async_test(function(t) {

p = Promise.reject(e);
postMessageTask(function() {
Promise.resolve().then(function() {
postMessageTask(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
Promise.resolve().then(function() {
p.catch(function() {});
Promise.resolve().then(function() {
p.catch(function() {});
});
});
});
});
});
});
}, 'delayed handling: postMessageTask after promise creation/rejection, plus many promise microtasks, is too late ' +
'to attach a rejection handler');
}, 'delayed handling: a nested-postMessageTask after promise creation/rejection, plus many promise microtasks, is ' +
'too late to attach a rejection handler');

async_test(function(t) {
var unhandledPromises = [];
var unhandledReasons = [];
var e = new Error();
var p;

var unhandled = function(ev) {
if (ev.promise === p) {
t.step(function() {
unhandledPromises.push(ev.promise);
unhandledReasons.push(ev.reason);
});
}
};
var handled = function(ev) {
if (ev.promise === p) {
t.step(function() {
assert_array_equals(unhandledPromises, [p]);
assert_array_equals(unhandledReasons, [e]);
assert_equals(ev.promise, p);
assert_equals(ev.reason, e);
});
}
};
addEventListener('unhandledrejection', unhandled);
addEventListener('rejectionhandled', handled);
ensureCleanup(t, unhandled, handled);

p = new Promise(function() {
throw e;
});
setTimeout(function() {
var unreached = t.unreached_func('promise should not be fulfilled');
p.then(unreached, function(reason) {
assert_equals(reason, e);
setTimeout(function() { t.done(); }, 10);
});
}, 10);
}, 'delayed handling: delaying handling by setTimeout(,10) will cause both events to fire');

//
// Miscellaneous tests about integration with the rest of the platform
Expand Down

0 comments on commit 2e069ec

Please sign in to comment.