Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shouldn't an exception inside Promise() constructor stop the rest of the script?

I'm studying the Promise() constructor, and I noticed something unexpected for my me.

console.log('first');
const promise1 = new Promise((resolve, reject) => {
  console.log('inside executor');
  let what = 1
  console.log(what());
  console.log('not reached');
  resolve('Hi Guys!');
});
console.log('continues'); // why does it continue?

Output:

first
inside executor
continues // whaaaaaaaaaaaaaaaaaaaaaaaat?????????
index.js:5 Uncaught (in promise) TypeError: what is not a function
  at index.js:5:15
  at new Promise (<anonymous>)

Expected output (I expect this output as the executor runs synchronously):

first
inside executor
index.js:5 Uncaught (in promise) TypeError: what is not a function
      at index.js:5:15
      at new Promise (<anonymous>)

The executor the constructor is said to run synchronously, so:

why does it log continues if it should stop the execution of the script after each exception (after console.log(what();)?

I understand that I should use e.g. catch() for the promise rejection but that is not the main point of the question.

like image 739
gus Avatar asked Dec 22 '25 09:12

gus


1 Answers

why does it log "continues" if it should stop the execution of the script after each exception (after console.log(what();))?

The error that occurs in the callback function will be caught by the internal promise implementation (which is the part that called your callback), and will set the promise's state to rejected. The execution of the callback really is interrupted as you would expect from a runtime error. But as the error is caught (handled), the caller will get the promise object back from the new Promise() call, and execution can happily continue and print "continues".

See the ECMAScript specification, at The Promise Constructor where executor is your callback function that triggers an error:

  1. Let completion be Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)).
  2. If completion is an abrupt completion, then
    a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »).
  3. Return promise.

The handling of an error happens in step 10: note how the procedure does not propagate that error further, but registers a rejection and continues with step 11, which is a normal completion.

We can imagine the internal promise constructor code to look somewhat like this (if it would be written in JavaScript), but simplified:

class Promise {
    #state
    #resolvedValue
    #customers

    constructor(executor) {
        // ... initialisation code ...
        this.#state = "pending";
        this.#customers = []; // Any `then`, `catch` calls would populate this
        //       ...
        try {
             executor((value) => this.#resolve(value), 
                      (reason) => this.#reject(reason));
        } catch(err) {
             // Absorb the error, and allow execution to continue
             this.#reject(err); 
        }
    }

    #reject(reason) {
        if (this.#state !== "pending") return; // Ignore
        this.#state = "rejected";
        this.#resolvedValue = reason;
        // Queue the asynchronous calls of any then/catch callbacks 
        //    if they exist (registered in this.#customers)
        this.#broadcast(); // I omit the implementation
    }

    // ... define #resolve, then, catch, #broadcast, ...
    // ...
}
like image 92
trincot Avatar answered Dec 23 '25 23:12

trincot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!