在平时的开发中经常使用到piramis,但我们使用的都是经过大佬封装后的成果,其实我们并不太了解真正的执行机制和顺序,这里我自己研究了下,下面做个记录并分享出来:
const PENDING = 'pending'; // 定义待定状态的常量
const FULFILLED = 'fulfilled'; // 定义已完成状态的常量
const REJECTED = 'rejected'; // 定义已拒绝状态的常量
class MyPiramis { // 定义 MyPiramis 类
#status = PENDING; // 私有字段,用于存储状态,默认为待定状态
#result = undefined; // 私有字段,用于存储结果,默认为未定义
#resolveCallbacks = []; // 私有数组,用于存储解决回调函数
#rejectCallbacks = []; // 私有数组,用于存储拒绝回调函数
constructor(executor) { // 构造函数
const resolve = (data) => { // 解决函数,用于将状态更改为已完成并执行解决回调函数
console.log(data, '函数resolve中的data'); // 记录解决函数中接收到的数据
setTimeout(() => { // 使用 setTimeout 模拟异步行为
console.log('执行函数resolve中setTimeout'); // 记录解决函数中的 setTimeout 执行
this.#changeStatus(FULFILLED, data); // 将状态更改为已完成
this.#resolveCallbacks.forEach(fn => fn(data)); // 执行所有解决回调函数
this.#resolveCallbacks = []; // 清空解决回调函数列表,避免内存泄漏
this.#rejectCallbacks = []; // 清空拒绝回调函数列表,避免内存泄漏
});
};
const rejected = (err) => { // 拒绝函数,用于将状态更改为已拒绝并执行拒绝回调函数
setTimeout(() => { // 使用 setTimeout 模拟异步行为
console.log('执行函数reject中setTimeout'); // 记录拒绝函数中的 setTimeout 执行
this.#changeStatus(REJECTED, err); // 将状态更改为已拒绝
this.#rejectCallbacks.forEach(fn => fn(err)); // 执行所有拒绝回调函数
this.#resolveCallbacks = []; // 清空解决回调函数列表,避免内存泄漏
this.#rejectCallbacks = []; // 清空拒绝回调函数列表,避免内存泄漏
});
};
try {
console.log('执行函数executor'); // 记录执行 executor 函数
executor(resolve, rejected); // 执行带有 resolve 和 rejected 函数的 executor 函数
} catch (err) {
console.log(err, '函数executor中的err'); // 记录 executor 函数中出现的任何错误
rejected(err); // 如果出现错误,则调用 rejected 函数
}
}
#changeStatus(status, data) { // 更改状态和结果的私有方法
console.log(this.#status,'设置状态'); // 记录设置状态
if (this.#status !== PENDING) return; // 如果状态不是待定,则不执行任何操作
this.#status = status; // 更新状态为提供的状态
this.#result = data; // 将结果设置为提供的数据
}
then(onFulfilled, onRejected) { // 注册完成和拒绝的回调函数的方法
console.log('执行函数中then方法'); // 记录 then 方法的执行
return new MyPiramis((resolve, rejected) => { // 返回 MyPiramis 的新实例
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => { }; // 确保 onFulfilled 是一个函数
onRejected = typeof onRejected === 'function' ? onRejected : () => { }; // 确保 onRejected 是一个函数
if (this.#status === PENDING) { // 如果状态是待定,则将回调函数添加到相应的数组中
console.log('函数中then方法的pending状态'); // 记录待定状态
this.#resolveCallbacks.push(onFulfilled); // 将 onFulfilled 添加到解决回调函数中
this.#rejectCallbacks.push(onRejected); // 将 onRejected 添加到拒绝回调函数中
}
if (this.#status === FULFILLED) { // 如果状态是已完成,则执行 onFulfilled
setTimeout(() => { // 使用 setTimeout 模拟异步行为
try {
console.log('函数中then方法的fulfilled状态的setTimeout中try'); // 记录 setTimeout 中的 try 块
onFulfilled(this.#result); // 执行 onFulfilled,并传递结果
} catch (err) {
console.log('函数中then方法的fulfilled状态的setTimeout中catch'); // 记录 setTimeout 中的 catch 块
rejected(err); // 如果出现错误,则调用 rejected 函数
}
});
}
if (this.#status === REJECTED) { // 如果状态是已拒绝,则执行 onRejected
setTimeout(() => { // 使用 setTimeout 模拟异步行为
try {
console.log('函数中then方法的rejected状态的setTimeout中try'); // 记录 setTimeout 中的 try 块
onRejected(this.#result); // 执行 onRejected,并传递结果
} catch (err) {
console.log('函数中then方法的rejected状态的setTimeout中catch'); // 记录 setTimeout 中的 catch 块
rejected(err); // 如果出现错误,则调用 rejected 函数
}
});
}
});
}
}
上边每一段代码都有注释,console.log的主要目的就是为了看清楚执行顺序。
下面展示下不同情况下的执行顺序:
let p = new MyPiramis((resolve, rejected) => { // 使用 executor 函数创建 MyPiramis 的新实例
console.log('进入p中'); // 记录进入 executor 函数
resolve('嘿哈'); // 使用 '嘿哈' 进行解决
// throw 'error'
// rejected('失败'); // 使用 '失败' 进行拒绝
})
p.then(
(data) => { // 完成处理程序
console.log(data, '成功data'); // 记录成功数据
},
(err) => { // 拒绝处理程序
console.log(err, '失败err'); // 记录错误
}
)
这里的执行打印结果为:
执行函数executor
进入p中
嘿哈 函数resolve中的data
执行函数中then方法
执行函数executor
函数中then方法的pending状态
执行函数resolve中setTimeout
pending 设置状态
嘿哈 成功data
let p = new MyPiramis((resolve, rejected) => { // 使用 executor 函数创建 MyPiramis 的新实例
console.log('进入p中'); // 记录进入 executor 函数
resolve('嘿哈'); // 使用 '嘿哈' 进行解决
throw 'error'
rejected('失败'); // 使用 '失败' 进行拒绝
})
p.then(
(data) => { // 完成处理程序
console.log(data, '成功data'); // 记录成功数据
},
(err) => { // 拒绝处理程序
console.log(err, '失败err'); // 记录错误
}
)
执行结果:
执行函数executor
进入p中
嘿哈 函数resolve中的data
error 函数executor中的err
执行函数中then方法
执行函数executor
函数中then方法的pending状态
执行函数resolve中setTimeout
pending 设置状态
嘿哈 成功data
执行函数reject中setTimeout
fulfilled 设置状态
这里可以对比着原始的piramis去试验一下,这样看执行过程就很清晰了。