为啥要写这篇文章?第一个是这个源码本身并不难,第二个是通过书写源码加深对promise的理解,最重要的一点是面试会遇到。
首先实现promise基本代码 Promise 作为构造函数 里面传递一个函数,传递的函数里面再传递两个函数,于是写出一下代码
class MyPromise {
constructor(excutor) {
const resolve = (res) => {};
const reject = (reason) => {};
excutor(resolve, reject);
}
}
由于Promise内部维护了三个状态,分别为Pending,Resolve,Rejected。且每个promise的状态一定是由pending转化为resolve 或者rejected 状态,且不可逆,并且由pending转化后的状态都会有对应的数据,于是改造代码,增加状态
const MyPromise = (() => {
//为了外部无法修改内部状态,这里使用IFFE 生成闭包,同时利用Sysbol 做私有属性
//这里定义三个状态,使用变量替代硬编码
const PENDING = "pending",
SUCCESS = "success",
Fail = "fail";
//这里记录当前promise的状态
const promiseStatu = Symbol("statu");
//这里记录 成功的数据 或者 失败的理由
const promiseValue = Symbol("promiseValue");
return class {
constructor(excutor) {
this[promiseStatu] = PENDING;
this[promiseValue] = undefined;
const resolve = (data) => {
this.changeStatu(SUCCESS, data);
};
const reject = (reason) => {
this.changeStatu(Fail, reason);
};
excutor(resolve, reject);
}
changeStatu(statu, data) {
//这里改变状态 只有状态是pending才改变
if (this[promiseStatu] === PENDING) {
this[promiseStatu] = statu;
this[promiseValue] = data;
}
}
};
})();
接下来我们实现then 方法。
const MyPromise = (() => {
//为了外部无法修改内部状态,这里使用IFFE 生成闭包,同时利用Sysbol 做私有属性
//这里定义三个状态,使用变量替代硬编码
const PENDING = "pending",
SUCCESS = "success",
Fail = "fail";
//这里记录当前promise的状态
const promiseStatu = Symbol("statu");
//这里记录 成功的数据 或者 失败的理由
const promiseValue = Symbol("promiseValue");
//这里保存 在then 函数里 res 和 rej 这两个函数
const promiseResCallback = Symbol("promiseResCallback");
const promiseRejCallback = Symbol("promiseRejCallback");
return class {
constructor(excutor) {
this[promiseStatu] = PENDING;
this[promiseValue] = undefined;
this[promiseResCallback] = [];
this[promiseRejCallback] = [];
const resolve = (data) => {
this.changeStatu(SUCCESS, data);
};
const reject = (reason) => {
this.changeStatu(Fail, reason);
};
excutor(resolve, reject);
}
then(res, rej) {
if (this[promiseStatu] === SUCCESS) {
setTimeout(() => {
res(this[promiseValue]);
}, 0);
} else if (this[promiseStatu] === Fail) {
setTimeout(() => {
rej(this[promiseValue]);
}, 0);
} else {
//pending 状态,这里如果是pending,那么then 参数里面的两个函数应该被保存起来,
//在之前的resolve,或者reject的函数 里面进行调用
this[promiseResCallback].push(res);
this[promiseRejCallback].push(rej);
}
}
catch(rej) {
if (this[promiseStatu] === Fail) {
rej(this[promiseValue]);
} else if (this[promiseStatu] === PENDING) {
this[promiseRejCallback].push(rej);
}
}
changeStatu(statu, data) {
//这里改变状态 只有状态是pending才改变
if (this[promiseStatu] === PENDING) {
this[promiseStatu] = statu;
this[promiseValue] = data;
if (statu === SUCCESS) {
this[promiseResCallback].forEach((element) => {
setTimeout(() => {
element(data);
}, 0);
});
} else {
this[promiseRejCallback].forEach((element) => {
setTimeout(() => {
element(data);
}, 0);
});
}
}
}
};
})();
then 方法的完善
- 第一是then方法的需要返回一个promise 实现链式调用? 这样的话一定是要在Promise的then方法里面返回一个新的Promise
- 第二是then方法如果实现then穿透? 可以对参数做默认处理,如果没有传递参数,可以默认一个参数,第一个 默认为 data=>data 第二个也可以做同样的默认, 不过在穿透时做一个值得比较,如果是undefined则无需穿透吗,否则穿透,于是最终生成代码
const MyPromise = (() => {
//为了外部无法修改内部状态,这里使用IFFE 生成闭包,同时利用Sysbol 做私有属性
//这里定义三个状态,使用变量替代硬编码
const PENDING = "pending",
SUCCESS = "success",
Fail = "fail";
//这里记录当前promise的状态
const promiseStatu = Symbol("statu");
//这里记录 成功的数据 或者 失败的理由
const promiseValue = Symbol("promiseValue");
//这里保存 在then 函数里 res 和 rej 这两个函数
const promiseResCallback = Symbol("promiseResCallback");
const promiseRejCallback = Symbol("promiseRejCallback");
return class {
constructor(excutor) {
this[promiseStatu] = PENDING;
this[promiseValue] = undefined;
this[promiseResCallback] = [];
this[promiseRejCallback] = [];
const resolve = (data) => {
this.changeStatu(SUCCESS, data);
};
const reject = (reason) => {
this.changeStatu(Fail, reason);
};
excutor(resolve, reject);
}
// then 这里除了涉及发布订阅 还涉及then穿透以及链式调用
then(res = (data) => data, rej = (reason) => reason) {
return new MyPromise((resolve, reject) => {
if (this[promiseStatu] === SUCCESS) {
//setTimeout 模拟微队列
setTimeout(() => {
try {
const result = res(this[promiseValue]);
if (result instanceof MyPromise) {
result.then(
(data) => {
resolve(data);
},
(reason) => {
reject(reason);
}
);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
}, 0);
} else if (this[promiseStatu] === Fail) {
setTimeout(() => {
rej(this[promiseValue]);
}, 0);
} else {
//pending 状态,这里如果是pending,那么then 参数里面的两个函数应该被保存起来,
//在之前的resolve,或者reject的函数 里面进行调用
this[promiseResCallback].push(() => {
try {
const result = res(this[promiseValue]);
if (result instanceof MyPromise) {
result.then(
(data) => {
resolve(data);
},
(reason) => {
reject(reason);
}
);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
}); //这里要知道res 执行的时机
this[promiseRejCallback].push(() => {
try {
const result = rej(this[promiseValue]);
if (result === undefined) {
resolve();
} else {
reject(result);
}
} catch (error) {
reject(error);
}
});
}
});
}
catch(rej) {
if (this[promiseStatu] === Fail) {
rej(this[promiseValue]);
} else if (this[promiseStatu] === PENDING) {
this[promiseRejCallback].push(rej);
}
}
changeStatu(statu, data) {
//这里改变状态 只有状态是pending才改变
if (this[promiseStatu] === PENDING) {
this[promiseStatu] = statu;
this[promiseValue] = data;
if (statu === SUCCESS) {
this[promiseResCallback].forEach((element) => {
setTimeout(() => {
element(data);
}, 0);
});
} else {
this[promiseRejCallback].forEach((element) => {
setTimeout(() => {
element(data);
}, 0);
});
}
}
}
};
})();
最后就是一个 Promise.all 和 Promise.rice,大家不妨思考该如何实现。