class MyPromise {
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
#status;
#value;
#reason;
#onFulfilledCallbacks = [];
#onRejectedCallbacks = [];
constructor(executor) {
this.#status = MyPromise.PENDING;
this.#value = undefined;
this.#reason = undefined;
try {
executor(this.#resolve.bind(this), this.#reject.bind(this));
} catch (error) {
this.#reject(error);
}
}
#resolve(value) {
if (this.#status === MyPromise.PENDING) {
this.#status = MyPromise.FULFILLED;
this.#value = value;
this.#onFulfilledCallbacks.forEach((fn) => {
fn(this.#value);
});
}
}
#reject(reason) {
if (this.#status === MyPromise.PENDING) {
this.#status = MyPromise.REJECTED;
this.#reason = reason;
this.#onRejectedCallbacks.forEach((fn) => {
fn(this.#reason);
});
}
}
then(onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
function nextTickFn(typeFn, value, onTypeFn, resolve, reject) {
queueMicrotask(() => {
try {
if (typeof onTypeFn !== "function") {
typeFn(value);
} else {
let x = onTypeFn(value);
resolvePromise(promise2, x, resolve, reject);
}
} catch (error) {
reject(error);
}
});
}
if (this.#status === MyPromise.FULFILLED) {
nextTickFn(resolve, this.#value, onFulfilled, resolve, reject);
} else if (this.#status === MyPromise.REJECTED) {
nextTickFn(reject, this.#reason, onRejected, resolve, reject);
} else if (this.#status === MyPromise.PENDING) {
this.#onFulfilledCallbacks.push(() => {
nextTickFn(resolve, this.#value, onFulfilled, resolve, reject);
});
this.#onRejectedCallbacks.push(() => {
nextTickFn(reject, this.#reason, onRejected, resolve, reject);
});
}
});
return promise2;
}
catch(onRejected) {
this.then(undefined, onRejected);
}
finally(onFinally) {
this.then(
() => {
onFinally();
},
() => {
onFinally();
}
);
}
static resolve(value) {
return new MyPromise((resolve) => {
resolve(value);
});
}
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
static all(promises) {
return new MyPromise((resolve, reject) => {
const values = [];
if (promises.length === 0) {
resolve(values);
return;
}
promises.forEach((promise) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
values.push(res);
if (values.length === promises.length) {
resolve(values);
}
},
(err) => {
reject(err);
}
);
} else {
values.push(promise);
if (values.length === promises.length) {
resolve(values);
}
}
});
});
}
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
const results = [];
if (promises.length === 0) {
resolve(results);
return;
}
promises.forEach((promise) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
results.push({ status: MyPromise.FULFILLED, value: res });
if (results.length === promises.length) {
resolve(results);
}
},
(err) => {
results.push({ status: MyPromise.REJECTED, reason: err });
if (results.length === promises.length) {
resolve(results);
}
}
);
} else {
results.push({ status: MyPromise.FULFILLED, value: promise });
if (results.length === promises.length) {
resolve(results);
}
}
});
});
}
static any(promises) {
return new MyPromise((resolve, reject) => {
const errors = [];
if (promises.length === 0) {
reject({
message: "All promises were rejected",
stack: "AggregateError: All promises were rejected",
errors: [],
});
return;
}
promises.forEach((promise) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
resolve(res);
},
(err) => {
errors.push(err);
if (errors.length === promises.length) {
reject({
message: "All promises were rejected",
stack: "AggregateError: All promises were rejected",
errors,
});
}
}
);
} else {
resolve(promise);
}
});
});
}
static race(promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) {
return;
}
promises.forEach((promise) => {
if (promise instanceof MyPromise) {
promise.then(
(res) => {
resolve(res);
},
(err) => {
reject(err);
}
);
} else {
resolve(promise);
}
});
});
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
throw new TypeError("Chaining cycle detected for promise");
}
if (x instanceof MyPromise) {
x.then((y) => {
resolvePromise(promise2, y, resolve, reject);
}, reject);
} else if (x !== null && (typeof x === "object" || typeof x === "function")) {
let then;
try {
then = x.then;
} catch (error) {
reject(error);
}
if (typeof then === "function") {
let called = false;
try {
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
resolve(x);
}
} else {
resolve(x);
}
}