废话不多说,直接上代码
// 先赞后看,已成习惯
// 定义状态码(这个不一定要和别人的一样,随便是啥都行)
const SUCCESS = "resolved";
const FAIL = "rejected";
const LOADING = "pending";
/**
* 符合A+规范的,参考MDN
*/
const isPromiseLike = (promise) => {
return (
promise &&
(typeof promise === "function" || typeof promise === "object") &&
typeof promise.then === "function"
);
};
class MyPromise {
#promiseValue = undefined;
#promiseStatus = LOADING;
#taskList = [];
constructor(task) {
if (!task || typeof task !== "function") {
return;
}
const resolve = (data) => {
this.#changeStatus(SUCCESS, data);
};
const reject = (err) => {
this.#changeStatus(FAIL, err);
};
try {
task(resolve, reject);
} catch (error) {
reject(error);
}
}
#changeStatus(status, value) {
// 状态只允许改变一次,后续的改变不会处理
if (#promiseStatus !== LOADING) {
return;
}
this.#promiseStatus = status;
this.#promiseValue = value;
this.#run();
}
// 微队列
#runMircoTask(callback) {
// 如果支持微队列API,直接放到里面执行
if (typeof queueMicrotask === 'function') {
queueMicrotask(callback);
return
}
// 如果支持MutationObserver
if (MutationObserver) {
const observer = new MutationObserver((mutations, observer) => {
if (mutations[0]) {
callback?.();
observer.disconnect();
}
})
const targetDom = document.createElement('div');
observer.observe(targetDom, { attributes: true });
targetDom.setAttribute('a', 'a');
return
}
// 如果支持setTimeout
if (typeof setTimeout === 'function') {
setTimeout(callback, 0);
return
}
// 都不支持,就直接调用吧
callback?.();
}
#runTask(task, resolve, reject) {
this.#runMircoTask(() => {
if (typeof task === "function") {
try {
const data = task(this.#promiseValue);
if (isPromiseLike(data)) {
data.then(resolve, reject);
} else {
resolve(data);
}
} catch (error) {
reject(error);
}
} else {
this.#promiseStatus === SUCCESS
? resolve(this.#promiseValue)
: reject(this.#promiseValue);
}
});
}
#run() {
// pending状态不执行回调函数,仅仅保存回调,将来当前Promise状态发生变化时会执行
if (this.#promiseStatus === LOADING) {
return;
}
while (this.#taskList.length > 0) {
const [successCallback, errorCallback, resolve, reject] =
this.#taskList.shift();
if (this.#promiseStatus === SUCCESS) {
this.#runTask(successCallback, resolve, reject);
} else {
this.#runTask(errorCallback, resolve, reject);
}
}
}
then(successCallback, errorCallback) {
return new MyPromise((resolve, reject) => {
this.#taskList.push([successCallback, errorCallback, resolve, reject]);
this.#run();
});
}
catch(errorCallback) {
return new MyPromise((resolve, reject) => {
// TODO
});
}
static resolve(data) {
return new MyPromise((_resolve) => {
_resolve(data)
})
}
static reject(data) {
return new MyPromise((_resolve, _reject) => {
_reject(data)
})
}
/**
* 确保所有Promise都成功完成才算成功,其中一个失败将会导致整个操作失败
*/
static all(promises) {
return new MyPromise((resolve, reject) => {
if (!promises || !Array.isArray(promises) || !promises.length) {
resolve([]);
return
}
let countResolved = 0;
const isAllResolved = () => promises.length === countResolved;
const result = new Array(promises.length).fill(undefined);
const check = () => {
if (isAllResolved()) {
resolve(result);
}
}
for(let i = 0; i < promises.length; i++) {
const curPromise = promises[i];
if (isPromiseLike(curPromise)) {
curPromise.then((data) => {
result[i] = data;
++countResolved;
check();
}, (error) => {
reject(error);
// 不阻塞后续的promise执行,但是结果没意义
continue;
})
} else {
result[i] = curPromise;
++countResolved;
check();
}
}
})
}
/**
* 处理所有Promise的结果,无论它们是否成功,并将结果包裹成一个对应的数组,且永远不会失败
*/
static allSettled(promises) {
return new MyPromise((resolve) => {
if (!promises || !Array.isArray(promises) || !promises.length) {
resolve([]);
return
}
let countResolved = 0;
const isAllResolved = () => promises.length === countResolved;
const result = new Array(promises.length).fill(undefined);
const check = (status, data) => {
++countResolved;
result[i] = { status, value: data };
if (isAllResolved()) {
resolve(result);
}
}
for(let i = 0; i < promises.length; i++) {
const curPromise = promises[i];
if (isPromiseLike(curPromise)) {
curPromise.then((data) => {
check(SUCCESS, data);
}, (error) => {
check(FAIL, error);
})
} else {
check(SUCCESS, curPromise);
}
}
})
}
/**
* 出现第一个完成状态的promise就完成了
*/
static race(promises) {
return new MyPromise((resolve) => {
if (!promises || !Array.isArray(promises) || !promises.length) {
resolve([]);
return
}
for(let i = 0; i < promises.length; i++) {
const curPromise = promises[i];
if (isPromiseLike(curPromise)) {
curPromise.then((data) => {
resolve(data);
}, (error) => {
resolve(error);
})
} else {
resolve(curPromise);
}
}
})
}
}