

<script>
function runAsyncTask(callback) {
if (typeof queueMicrotask === "function") {
queueMicrotask(callback);
} else if (typeof MutationObserver === "function") {
const obs = new MutationObserver(callback);
const divNode = document.createElement("div");
obs.observe(divNode, { childList: true });
divNode.innerText = "fx666";
} else {
setTimeout(callback, 0);
}
}
function resolvePromise(p2, x, resolve, reject) {
if (x === p2) {
throw new TypeError("Chaining cycle detected for promise #<Promise>");
}
if (x instanceof FXPromise) {
x.then(
(res) => resolve(res),
(err) => reject(err)
);
} else {
resolve(x);
}
}
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class FXPromise {
state = PENDING;
result = undefined;
#handlers = [];
constructor(fn) {
const resolveFunc = (res) => {
if (this.state === PENDING) {
this.result = res;
this.state = FULFILLED;
this.#handlers.forEach(({ onFulfilled }) => {
onFulfilled(this.result);
});
}
};
const rejectFunc = (res) => {
if (this.state === PENDING) {
this.result = res;
this.state = REJECTED;
this.#handlers.forEach(({ onRejected }) => onRejected(this.result));
}
};
try {
fn(resolveFunc, rejectFunc);
} catch (error) {
rejectFunc(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (x) => x;
onRejected =
typeof onRejected === "function"
? onRejected
: (x) => {
throw x;
};
const p2 = new FXPromise((resolve, reject) => {
if (this.state === FULFILLED) {
runAsyncTask(() => {
try {
const x = onFulfilled(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === REJECTED) {
runAsyncTask(() => {
try {
const x = onRejected(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === PENDING) {
this.#handlers.push({
onFulfilled: (res) => {
runAsyncTask(() => {
try {
const x = onFulfilled(res);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
},
onRejected: (res) => {
runAsyncTask(() => {
try {
const x = onRejected(res);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
},
});
}
});
return p2;
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(onFinally) {
return this.then(onFinally, onFinally);
}
}
const p = new FXPromise((resolve, reject) => {
throw "throw-error";
});
p.then((res) => {
console.log("res: ", res);
})
.catch((e) => {
console.log("catch: ", e);
})
.finally(() => {
console.log("finally");
});
</script>