Promises: unhandled rejections and finally()

# Felipe Gasper (6 months ago)

Hello,

In node 12 as well as the latest Chrome and FF (all on macOS) I see the following:


var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); n(789); ==> produces 1 unhandled-rejection warning

var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () => {} ); n(789); ==> produces 1 unhandled-rejection warning

var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () => {} ); p.finally( () => {} ); n(789); ==> produces 2 unhandled-rejection warnings

var y,n; var p = new Promise( (yy,nn) => { y=yy; n=nn } ); p.finally( () => {} ); p.finally( () => {} ); p.finally( () => {} ); n(789); ==> produces 3 unhandled-rejection warnings


Could someone point me to something that would help me to understand the logic here? It looks like the first finally() is getting a “free pass” while only the 2nd and subsequent ones trigger their own unhandled-rejection warnings.

Thank you!

# Logan Smyth (6 months ago)

Could someone point me to something that would help me to understand the

logic here? It looks like the first finally() is getting a “free pass” while only the 2nd and subsequent ones trigger their own unhandled-rejection warnings.

I think the best place to start in understanding this would be to step back and make sure you understand what it is that triggers these errors. Node triggers these errors when a promise object has been rejected but has no handlers to do respond to the rejection. I forget exactly what point Node checks for handlers these days, if that point is at the end of the job execution or on GC of promises now or what but that's not important for this case.

Let's look at your example. I'm also simplifying it to

var p = Promise.reject(789);
var one = p.finally(() => {});

var two = p.finally(() => {});

var three = p.finally(() => {});
var p = Promise.reject(789);

There is only one promise here, p, and it has no handlers ever attached to it so there is nothing to handle the rejection error, hence the single uncaught rejection error.

var p = Promise.reject(789);
var one = p.finally(() => {});

There are 2 promises here, p, which has one handler (the finally that will take the rejection of p and in turn reject one) and one, which has no handlers attached, so you again get a single uncaught rejection. It as not that the first "finally" gets a "free pass", it is that rejections from p are no longer uncaught, but you have added a new promise that is uncaught, so the overall number of uncaught rejections does not change.

var p = Promise.reject(789);
var one = p.finally(() => {});

var two = p.finally(() => {});

Hopefully you can see where this is going. p now has 2 handlers attached so its rejection isn't uncaught, but now both one and two have no handlers, so both will trigger an uncaught rejection error.

var p = Promise.reject(789);
var one = p.finally(() => {});

var two = p.finally(() => {});

var three = p.finally(() => {});

And finally now we have one, two and three all with no handlers attached, so you will get three uncaught rejection errors.

Hope that helps!

# Felipe Gasper (6 months ago)

Thank you .. that makes sense. I’m not sure why now but I had in mind that finally() creates a “passthrough” promise that doesn’t affect its parent promise’s resolved/rejected status.

# Jordan Harband (6 months ago)

It does pass through the fulfillment status - but it does that by adding both an onFulfilled and an onRejected handler, so it does affect the "unhandled rejection" hook.