try-catch не ловит ошибку

Описание

Написал простое допорлнение:

declare global {
    interface Promise<T> {
        isFulfilled: Promise<boolean>;
    }
}

Object.defineProperty(Promise.prototype, "isFulfilled", {
    async get<T>(this: Promise<T>): Promise<boolean> {
        const symbol = Promise.resolve(Symbol());
        try {
            const result = (await Promise.race([this, symbol]) !== symbol);
            console.log("after promise");
            return result;
        } catch {
            console.log(`catch`);
            return true;
        }
    }
});

const promise = new Promise<void>(async (resolve) => {
    throw new Error(`Test error`);
});
console.log(await (promise.isFulfilled));

export { };

Как видим выполнение Promise-а происходит в try и не выходит за него. В таком случае при ошибке try должен его ловить и перейти к catch.
Я и получаю ошибку и продолжается выполнение try:

Вопрос

Почему так происходит?
Как исправить мою функцию.


Ответы (1 шт):

Автор решения: Alexandroppolus

Тут, наверно, правильнее было бы назвать isSettled, если судить по коду. Fulfilled - это когда промис завершен успешно, а у тебя проверяется, что он в принципе завершен, т.е. не pending. Но это смотри сам.

Ошибки сразу в нескольких местах. Во первых, непонятно зачем надо было резолвить симбол. Теперь там промис, и сравнение "!== symbol" всегда будет true.

Во вторых, тестовый пример. В конструктор промиса передана async-функция. В итоге она сама закетчила исключение и вернула отклоненный промис, который не был обработан и потому вывалил ошибку в консоль. А тот промис, который ты создал через конструктор, навсегда остался пендингом, ведь в переданной в конструктор функции не было необработанного исключения (и функции resolve/reject никто не вызвал). Далее в isFulfilled race вернул симбол, который, как и было сказано выше, не равен промису с симболом. Итого true вместо false.

Исправленный вариант для isFulfilled и тестовые примеры:

Object.defineProperty(Promise.prototype, "isFulfilled", {
    get() {
        const s = Symbol();
        return Promise.race([this, s]).then(v => v !== s, () => true);
    }
});

Promise.resolve().isFulfilled.then(console.log); // true
Promise.reject().isFulfilled.then(console.log); // true

new Promise(() => {}).isFulfilled.then(console.log); // false

new Promise((res) => {
    setTimeout(res, 100)
}).isFulfilled.then(console.log); // false

new Promise(() => {
    throw new Error('err');
}).isFulfilled.then(console.log); // true

new Promise((res) => {res();}).isFulfilled.then(console.log); // true
→ Ссылка