学习链接
BAT前端经典面试问题:史上最最最详细的手写Promise教程(代码来源,其他链接是为了当收藏家(
手把手一行一行代码教你“手写Promise“,完美通过 Promises/A+ 官方872个测试用例
Promise
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onResolvedCallback = [];
this.onRejectedCallback = [];
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onResolvedCallback.forEach(fn => fn());
}
};
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallback.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
const promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
queueMicrotask(() => {
try {
let result = onFulfilled(this.value);
resolvePromise(promise2, result, resolve, reject);
} catch (error) {
reject(error);
}
});
}
if (this.state === 'rejected') {
queueMicrotask(() => {
try {
let result = onRejected(this.reason);
resolvePromise(promise2, result, resolve, reject);
} catch (error) {
reject(error);
}
});
}
if (this.state === 'pending') {
this.onResolvedCallback.push(() => {
queueMicrotask(() => {
try {
let result = onFulfilled(this.value);
resolvePromise(promise2, result, resolve, reject);
} catch (error) {
reject(error);
}
});
});
this.onRejectedCallback.push(() => {
queueMicrotask(() => {
try {
let result = onRejected(this.reason);
resolvePromise(promise2, result, resolve, reject);
} catch (error) {
reject(error);
}
});
});
}
})
return promise2; // 完成链式调用
}
}
function resolvePromise(promise2, result, resolve, reject) {
// resolve 和 reject 是 promise2 实例构建时候的参数方法
// result 是then方法参数中传入的回调函数 的返回结果
if (result === promise2) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 防止 resolve reject 被多次调用(catch)
let called = false;
if (result === null || (typeof result !== 'object' && typeof result !== 'function')) {
return resolve(result);
}
try {
let then = result.then;
if (typeof then !== 'function') {
return resolve(result);
}
then.call(result, value => {
if (called) return;
called = true;
resolvePromise(promise2, value, resolve, reject);
}, reason => {
if (called) return;
called = true;
reject(reason);
})
} catch (error) {
if (called) return;
called = true;
reject(error);
}
}
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
resolve(value);
});
}
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
Promise.all = promiseArr => {
return new Promise((resolve, reject) => {
if (!promiseArr[Symbol.iterator]) {
throw new TypeError(`${promiseArr} is not iterable`) // 需要iterator接口
}
if (!Array.isArray(promiseArr)) {
promiseArr = Array.from(promiseArr); // 根据 iterator 接口转为数组
}
let result = [], count = 0; // count 记录fulfilled数量
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(res => { // 转为 Promise 对象
count++; // 进入then的回调, fulfilled的实例数加一
result[i] = res;
if (count === promiseArr.length) {
resolve(result);
}
}, err => {
reject(err);
})
})
})
}
Promise.any = promiseArr => {
return new Promise((resolve, reject) => {
if (!promiseArr[Symbol.iterator]) {
throw new TypeError(`${promiseArr}is not iterable`);
}
if (!Array.isArray(promiseArr)) {
promiseArr = Array.from(promiseArr);
}
const result = [];
let count = 0;
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(
value => {
resolve(value);
},
reason => {
count++;
result[i] = reason;
if (count === promiseArr.length) {
reject(new AggregateError(result, 'All Promises were rejected'));
}
}
)
});
})
}
Promise.race = promiseArr => {
return new Promise((resolve, reject) => {
if (!promiseArr[Symbol.iterator]) {
throw new TypeError(`${promiseArr} is not iterable`);
}
if (!Array.isArray(promiseArr)) {
Array.from(promiseArr);
}
promiseArr.forEach(element => {
Promise.resolve(element).then(resolve, reject);
});
});
};
Promise.allSettled = promiseArr => {
return new Promise((resolve, reject) => {
if (!promiseArr[Symbol.iterator]) {
throw new TypeError(`${promiseArr} is not iterable`);
}
if (!Array.isArray(promiseArr)) {
promiseArr = Array.from(promiseArr);
}
const resArr = [];
let count = 0;
promiseArr.forEach((element, index) => {
Promise.resolve(element).then(
valule => {
resArr[index] = {
status: 'fulfilled',
valule
}
count++;
if (count === promiseArr.length) resolve(resArr);
},
reason => {
resArr[index] = {
status: 'rejected',
reason
}
count++;
if (count === promiseArr.length) resolve(resArr);
}
);
});
})
};
Promise.prototype.finally = callback => {
return this.then(
value => Promise.resolve(callback()).then(() => value),
reason => Promise.resolve(callback()).then(() => { throw reason; })
)
};