1. 明确需求
在 Promise 中,then 方法和 catch 方法都是可以通过链式调用这种形式无限调用下去的。
所以考虑以下几个改造点:
- then方法中应该直接把 this 给 return 出去(链式调用常规操作);
- 链式调用允许我们多次调用 then,多个 then 中传入的 onResolved(也叫onFulFilled) 和 onRejected 任务,我们需要把它们维护在一个队列里;
- 要想办法确保 then 方法执行的时机,务必在 onResolved 队列 和 onRejected 队列批量执行前。不然队列任务批量执行的时候,任务本身都还没收集完。一个比较容易想到的办法就是把批量执行这个动作包装成异步任务,这样就能确保它一定可以在同步代码之后执行了。
2. 编码
2.1 构造函数
function CutePromise(executor) {// value 记录异步任务成功的执行结果this.value = null;// reason 记录异步任务失败的原因this.reason = null;// status 记录当前状态,初始化是 pendingthis.status = 'pending';// 缓存两个队列,维护 resolved 和 rejected 各自对应的处理函数this.onResolvedQueue = [];this.onRejectedQueue = [];// 把 this 存下来,后面会用到var self = this;// 定义 resolve 函数function resolve(value) {// 如果不是 pending 状态,直接返回if (self.status !== 'pending') {return;}// 异步任务成功,把结果赋值给 valueself.value = value;// 当前状态切换为 resolvedself.status = 'resolved';// 用 setTimeout 延迟队列任务的执行setTimeout(function(){// 批量执行 resolved 队列里的任务self.onResolvedQueue.forEach(resolved => resolved(self.value));});}// 定义 reject 函数function reject(reason) {// 如果不是 pending 状态,直接返回if (self.status !== 'pending') {return;}// 异步任务失败,把结果赋值给 valueself.reason = reason;// 当前状态切换为 rejectedself.status = 'rejected';// 用 setTimeout 延迟队列任务的执行setTimeout(function(){// 批量执行 rejected 队列里的任务self.onRejectedQueue.forEach(rejected => rejected(self.reason));});}// 把 resolve 和 reject 能力赋予执行器try{execute(resolve, reject)}catch(e){reject(e)}}
2.2 then 方法
// then 方法接收两个函数作为入参(可选)CutePromise.prototype.then = function(onResolved, onRejected) {// 注意,onResolved 和 onRejected必须是函数;如果不是,我们此处用一个透传来兜底if (typeof onResolved !== 'function') {onResolved = function(x) {return x};}if (typeof onRejected !== 'function') {onRejected = function(e) {throw e};}// 依然是保存 thisvar self = this;// 判断是否是 resolved 状态if (self.status === 'resolved') {// 如果是 执行对应的处理方法onResolved(self.value);} else if (self.status === 'rejected') {// 若是 rejected 状态,则执行 rejected 对应方法onRejected(self.reason);} else if (self.status === 'pending') {// 若是 pending 状态,则只对任务做入队处理self.onResolvedQueue.push(onResolved);self.onRejectedQueue.push(onRejected);}return this};
3. 测试
const cutePromise = new CutePromise(function (resolve, reject) {resolve('成了!');});cutePromise.then((value) => {console.log(value)console.log('我是第 1 个任务')}).then(value => {console.log('我是第 2 个任务')});// 依次输出“成了!” “我是第 1 个任务” “我是第 2 个任务”

