How exactly work engine that throws "Unhandled rejection in promise" ?

Hi, folks.

Am trying to implement Promise in other programming language, and i almost succeded except one thing - i cant understand how to throw exception when promise processing is finished with rejected promise...

Example

Promise.resolve() // promise_id: 0
  ->then(val => { // promise_id: 1
    throw new Error();
  })
  ->then(val => { // promise_id: 2
     // never run cause of throw before
  })
  ->catch(reason => { // promise_id: 3
     console.log(reason); // it works for now!

     # no return means returned undefined/null - but means handled rejection
  })
  // more then-catch-finallies but it's never called because of bug in my code (btw the chain is built and they're called as initializers, but the handlers never arrive to the loop because there's triggered "is_handled" checker that throws because `pid:1` has no any catch handler
  ->catch() // promise_id: 4
  ->then() // promise_id: 5
  ->then() // promise_id: 6
  ->catch() // promise_id: 7

So we have 7 promises in chain. But we have only one MICROTASK in queue (because at the start only PROMISE 0 is settled, so first then is never become children and immediate become to loop), so loop is filled with p0->then() that is actually promise_id: 1

Once this microtask is rejected cause of throw statement

It calls reject that will add additional task to check is this promise is handled in next event loop tick

But looking for the chain we saw that next handler is THEN and only 3rd one is CATCH.

So this promise following logic of "handled" never receive catch handler at all, but pass rejected state to next level of chain.

Am not fully understand how to mark parent promise resolved or catched just because somewhere in its childrens we have a catch statement.

Especially understand that promises will never arrive any thing like tree and contain only childrens (reactions) and only until they're settled (then childrens removed)

How to mark all promises handled because somewhere in childrens presented handler ? My code throws UNHANDLED REJECTION IN PROMISE ID 1, because catch is attached only to id 2, but the parent rejects it too (following the chain)

"Passing on the state" (whether that's a fulfillment value or a rejection reason) happens via a handler as well; it is attached during the .then() or .catch() call. Any promise on which .then() or .catch() were called is marked as handled. It doesn't matter whether there is a callback to handle rejections anywhere - the "unhandled rejection" can only happen at the end of a chain.

1 Like

This is the most important part to understand: the locality of whether a promise is handled or not.

Adding a reaction to a promise designates it as handled, regardless of whether the promise has settled yet or not.

A reaction is added explicitly by calling then, catch or finally on it, or implicitly when using the promise as a resolution value for another promise (which internally calls then on the promise). Awaiting a promise does the same.

It doesn't matter if an explicit then call only provides a fulfillment callback because the then call returns a new promise which starts unhandled. So the original promise is handled and the new one which would reject isn't. Same concept for finally.

A reaction may be added to a promise after it has settled in a rejection. In those cases the spec will notify the host through a hook when the promise without reactions does reject, and later when a reaction is added. Some hosts reify these as events, others rely on garbage collection to trigger an exception when the promise can never become handled anymore (because it was collected).

1 Like