Confused about this .then/.finally error handling example

Here is a wierd behaviour that’s been bugging me for a whole day. All the usual docs and even AI explanations don’t make sense - so i have a feeling this might be something actually explained in the spec.

This throws synchronous error -

const taskFn = () => {
    throw new Error('sync error');
};

const {promise, resolve, reject} = Promise.withResolvers();
promise.catch(() => {});

const taskPromise = Promise.resolve().then(() => taskFn());
taskPromise
  .then(resolve, reject);
taskPromise
  .finally(() => {
  });

while this doesn’t -

const taskFn = () => {
    throw new Error('sync error');
};

const {promise, resolve, reject} = Promise.withResolvers();
promise.catch(() => {});

const taskPromise = Promise.resolve().then(() => taskFn());
taskPromise
  .then(resolve, reject)
  .finally(() => {
  });

The only difference is that second one has finally chained after then.
Why does this matter, since finally doesn’t handle any error?

(Sorry if it’s a very basic question)

The error in your first example is not a sync error (despite the error message :wink:). Its an unhandled promise rejection.

This unhandled rejection is coming from the promise created by the finally method. finally callbacks do not handle promise rejections and instead forward them on through the chain. So the promise returned by the finally is also getting rejected and remains unhandled thereby creating the error. If you handled that returned promises’s rejection with a catch the error would go away.

The fact that you handled the error in the original taskPromise doesn’t prevent it from passing that error through to other promises (e.g. via finally) which would also have to account for the rejection being handled.

Ahh, that makes so much sense. Finally would create a new promise, which is unhandled.

Yes, the error message also added to the confusion.

Thank you.