const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejectd) {
onFulfilled = isFunction(onFulfilled) ? onFulfilled : v => v;
onRejectd = isFunction(onRejectd) ? onRejectd : e => { throw e };
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
});
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejectd(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
});
} else if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejectd(this.reason);
resolvePromise(x, promise2, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
});
return promise2;
}
catch(onRejectd) {
return this.then(null, onRejectd);
}
finally(callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason });
});
}
}
MyPromise.deferred = function () {
let dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
MyPromise.resolve = function (value) {
if (value instanceof MyPromise) {
return value;
}
if (isThenable(value)) {
return new MyPromise((resolve, reject) => {
value.then(resolve, reject);
});
}
return new MyPromise((resolve) => {
resolve(value);
});
};
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
if (!isIterable(promises)) {
return reject(new TypeError('Arguments is not iterable'));
}
let index = -1;
let count = 0;
const result = [];
const length = typeof promises.length === 'number' ? promises.length : promises.size;
if (length === 0) {
return resolve(result);
}
function processSuccess(i, value) {
result[i] = value;
if (++count === length) {
resolve(result);
}
}
for (let p of promises) {
let i = ++index;
if (isThenable(p)) {
p.then(value => {
processSuccess(i, value);
}, reject);
} else {
processSuccess(i, p);
}
}
});
};
MyPromise.allSettled = function (promises) {
return new MyPromise((resolve, reject) => {
if (!isIterable(promises)) {
return reject(new TypeError('Arguments is not iterable'));
}
let index = -1;
let count = 0;
const result = [];
const length = typeof promises.length === 'number' ? promises.length : promises.size;
if (length === 0) {
return resolve(result);
}
function processSuccess(i, valueOrReason, status) {
result[i] = {
status,
[status === 'fulfilled' ? 'value' : 'reason']: valueOrReason,
};
if (++count === length) {
resolve(result);
}
}
for (let p of promises) {
let i = ++index;
if (isThenable(p)) {
p.then(value => {
processSuccess(i, value, 'fulfilled');
}, reason => {
processSuccess(i, reason, 'rejected');
});
} else {
processSuccess(i, p, 'fulfilled');
}
}
});
};
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
if (!isIterable(promises)) {
return reject(new TypeError('Arguments is not iterable'));
}
for (let p of promises) {
MyPromise.resolve(p).then(resolve, reject);
}
});
};
MyPromise.any = function (promises) {
return new MyPromise((resolve, reject) => {
if (!isIterable(promises)) {
return reject(new TypeError('Arguments is not iterable'));
}
let count = 0;
const errors = [];
const length = typeof promises.length === 'number' ? promises.length : promises.size;
if (length === 0) {
return reject(new AggregateError(errors, 'All promises were rejected'));
}
function countError(err) {
errors.push(err);
if (++count === length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
for (let p of promises) {
MyPromise.resolve(p).then(resolve, countError);
}
});
};
function isObject(value) {
return typeof value === 'object' && value !== null;
}
function isFunction(value) {
return typeof value === 'function';
}
function isThenable(value) {
if (isObject(value) || isFunction(value)) {
try {
let then = value.then;
return isFunction(then);
} catch (e) {
return false;
}
}
return false;
}
function isIterable(value) {
return isFunction(value[Symbol.iterator]);
}
function resolvePromise(x, promise2, resolve, reject) {
if (x === promise2) {
return reject(new TypeError('x === promise2'));
}
if (isObject(x) || isFunction(x)) {
let called = false;
try {
let then = x.then;
if (isFunction(then)) {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(y, promise2, resolve, reject);
}, e => {
if (called) return;
called = true;
reject(e);
});
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
module.exports = MyPromise;