手动实现Promise及all() race()
// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
class MyPromise {
constructor(fn) {
if (typeof fn !== 'function')
throw new TypeError('The first parameter of MyPromise must be a function!');
this.state = PENDING;
this.value = null;
this.reason = null;
this.resolvedCallbacks = [];
this.rejectedCallbacks = [];
this.resovle = value => {
if (this.state === PENDING) {
this.state = RESOLVED;
this.value = value;
setTimeout(() => {
this.resolvedCallbacks.forEach(cb => cb());
}, 0);
}
}
this.reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
setTimeout(() => {
this.rejectedCallbacks.forEach(cb => cb());
}, 0);
}
}
try {
fn(this.resovle, this.reject);
} catch (err) {
this.reject(err);
}
}
then(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let newPromise = null;
if (this.state === RESOLVED) {
return (newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
const x = onResolved(this.value);
this.resolutionProcedure(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
}))
}
else if (this.state === REJECTED) {
return (newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolutionProcedure(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
}))
}
else if (this.state === PENDING) {
return (newPromise = new Promise((resolve, reject) => {
this.resolvedCallbacks.push(() => {
try {
const x = onResolved(this.value);
this.resolutionProcedure(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
})
this.rejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
this.resolutionProcedure(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
})
}))
}
}
catch(onRejected) {
const onResolved = v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let newPromise = null;
if (this.state === RESOLVED) {
return (newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
resolve(this.value);
} catch (err) {
reject(err);
}
}, 0);
}))
}
else if (this.state === REJECTED) {
return (newPromise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
this.resolutionProcedure(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
}, 0);
}))
}
else if (this.state === PENDING) {
return (newPromise = new Promise((resolve, reject) => {
this.resolvedCallbacks.push(() => {
try {
resolve(this.value);
} catch (err) {
reject(err);
}
})
this.rejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
this.resolutionProcedure(newPromise, x, resolve, reject);
} catch (err) {
reject(err);
}
})
}))
}
}
resolutionProcedure(newPromise, x, resolve, reject) {
if (newPromise === x)
return reject(new TypeError('循环引用'));
if (x instanceof MyPromise) {
if (x.state === PENDING) {
x.then(value => {
x.resolutionProcedure(newPromise, value, resolve, reject);
}, reject);
} else {
x.then(resolve, reject);
}
return;
}
resolve(x);
}
}
MyPromise.all = function (promsArr) {
if (!(promsArr instanceof Array))
throw new TypeError('The first parameter of MyPromise.all() muset be an Array');
let n = promsArr.length;
let values = [];
return new MyPromise((resolve, reject) => {
let successCount = 0;
promsArr.forEach((prom, index) => {
if (!(prom instanceof MyPromise))
return;
if (prom.state === RESOLVED) {
values[index] = prom.value;
if (++successCount === n) {
resolve(values);
}
} else if (prom.state === REJECTED) {
reject(prom.reason);
} else {
prom.then(v => {
values[index] = v;
if (++successCount === n) {
resolve(values);
}
}, err => reject(err))
}
})
})
}
MyPromise.race = function (promsArr) {
if (!(promsArr instanceof Array))
throw new TypeError('The first parameter of MyPromise.all() muset be an Array');
let n = promsArr.length;
let failedCount = 0;
return new MyPromise((resolve, reject) => {
promsArr.forEach(prom => {
if (prom.state === RESOLVED) {
resolve(prom.value);
} else if (prom.state === REJECTED) {
if (++failedCount === n) {
reject(prom.reason);
}
} else {
prom.then(v => {
resolve(v);
}, reason => {
if (++failedCount === n) {
reject(prom.reason);
}
})
}
})
})
}