Urooj
Urooj

Reputation: 354

Synchronously rejecting a promise

This is how I thought the reject method works inside an executor function: It sets the state of the promise as rejected and then pushes all the callbacks registered through the .catch method or through second argument of .then method of promise to the microtask queue. If it finds that no callbacks are attached it throws an error. But then I was playing around and realized that this understanding is wrong. Consider the following code snippets:

Code 1:

    const p = new Promise((resolve, reject) => reject(9));

Code 2:

    const p = new Promise((resolve, reject) => reject(9));
    p.catch(e=>console.log(e));

Code 1 throws an error about unhandled exception. But code 2 does not. According to my flawed understanding, in code 2, since promises are eager, the executor function will be executed synchronously and then since I am rejecting the promise synchronously, at that point the reject function will not find any error handlers registered yet so an error will be thrown and the line(p.catch) shouldn't be executed. But this is not the case as the output of Code 2 is 9 being logged to the console.

How does reject work internally?

Edit: The comments and answer mentioned that the check for unhandled rejections does not happen until after the control is returned back to the event loop. If at that point no error handlers have been registered, unhandled exception error will be thrown. Consider the following code :

const p = new Promise ((resolve, reject) => reject("bad"));
setTimeout(()=>{
            p.catch((e)=>{
                console.log(e+" caught")
            });
        },0)

The promise is rejected, after that setTimeout is executed and control returns to event loop. In the meanwhile timer expired immediately and setTimeout callback was pushed to callback queue. So at this point no error callback is registered and the control has passed back to event loop so unhandled exception should be thrown. But "bad caught" is printed to console. How?

Upvotes: 0

Views: 108

Answers (1)

jfriend00
jfriend00

Reputation: 707976

The executor function is executed synchronously and the promise does change state immediately, but the test for unhandled rejections in the system does not happen after you return control back to the event loop.

So, the statement p.catch() runs and thus a catch handler gets registered synchronously (but the catch handler itself is not called yet) BEFORE the system checks to see if there is an unhandled rejection.

Among other reasons, this specifically allows you to write code like your second example since you couldn't possibly assign a .catch() handler until after the promise was created anyway.

Likewise, the rejection itself does cause reject handlers to be processed until you return control back to the event loop (catch handlers aren't executed synchronously). Just like .then() handlers, they always wait until the currently executing thread of execution has finished its synchronous execution and returned control back to the system (thus allowing your .catch() to be registered before the promise rejection is "processed").

Upvotes: 1

Related Questions