为了更好的理解promise 参考材料手写promise
手写promise一共两个核心东西
1:promise构造器
2:.then方法
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class MyPromise {
// 要做一个不让外面使用的 私有属性(内部属性)光靠_命名方式是不可靠的
// 方案1 使用ES6的 symbol
// 方案2 使用私有属性 在名字前面加#
// _state = "pending";
// _result = undefined;
#state = PENDING;
#result = undefined;
// 需要一个队列
#thenables = [],
// 接收一个参数 立即执行
constructor(executor) {
const resolve = (data) => {
// 因为promise的状态一旦确定下来之后,他就无法被更改了
// 所以需要判断一下
// 说明这个状态已经被更改过了 那就什么都不做
this.#changeState(FULFILLED,data)
};
const reject = (err) => {
this.#changeState(REJECTED,err)
};
// 只能捕获同步错误
try{
executor(resolve, reject);
}catch (err) {
reject(err);
}
}
#changeState(state,result){
if(this.#state !== PENDING) return ;
this.#state = state;
this.#result = result
// 打印后只有完成没有拒绝 -- 只打印第一个
console.log(this.#state,this.#result)
// 当状态改变的时候需要处理队列
this.#run();
}
// callback 什么时候运行
#handleCallback(callback ,resolve , reject ){
if(typeof callback !== 'function'){
// 状态穿透 就是把返回的那个promise的状态跟当前Promise跟当前的promise保持一致
// if(this.#state === FULFILLED){
// resolve(this.#result)
// }else {
// reject(this.#result)
// }
// 另一种写法
// 不过状态穿透这个事是放在微队列里执行的 所以需要
queueMicrotask(()=>{
const settled = this.#state === FULFILLED ? resolve : reject
settled(this.#result)
})
return;
}
// 如果回调是一个函数 需要执行这个函数 也需要放入微队列中
queueMicrotask(()=>{
// 接收当前的这个结果 -- 结果就是数据或者是失败的原因
// 那么返回的是resolve还是reject呢? 就需要看执行过程
try{
// 如果没问题的话 就把返回结果作为数据 来一个resolve
const data = callback(this.#result);
resolve(data);
}catch(err){
// 执行失败
reject(err);
}
})
}
#run () {
// 首先判断当前状态
// 如果当前状态是pending ,就直接结束了
if(this.#state === PENDING) return ;
// 如果不是正在处理中(已经有结果了要不是成功,要不就是拒绝),就处理队列,先进先出 一个一个取出来,然后依次执行
// 如果这个队列有东西
while(this.#thenables.length){
// 就一个一个取出来
const { onFulfilled,onRejected,resolve,reject } = this.#thenables.shift();
// 当前状态是完成状态
if(this.#state === FULFILLED){
// 执行onFulfilled
this.#handleCallback(onFulfilled,resolve,reject);
}else {
// 执行onRejected
this.#handleCallback(onRejected,resolve,reject);
}
}
}
// onFulfilled 、 onRejected 、 resolve 、reject 并不是立即处理的
// 以后如果遇到函数的执行并不是立即执行的 建议使用队列
then(onFulfilled , onRejected){
// then方法一定会返回一个新的promise
return new MyPromise((resolve, reject) => {
// 这里需要做的就是往这个队列里把这四个东西加进去,以便将来处理
this.#thenables.push({
onFulfilled ,
onRejected ,
resolve ,
reject ,
});
// 启动队列处理
// 什么时候启动这个#run方法
// 1:调用then方法,加入队列了
// 2:resolve和reject的时候
this.#run();
})
}
}
const p = new MyPromise((resolve, reject)=>{
// 如果先返回resolve 后返回reject reject(2);就失效了
// 因为promise的状态一旦确定下来之后,他就无法被更改了
resolve(1);
reject(2);
})
// 如果执行期间promise了,会更改当前状态
// const p = new MyPromise((resolve, reject)=>{
// setTimeout(()=>{
// resolve(123)
// },100)
// })
// 两个参数 第一个参数 完成时运行,第二个参数失败的时候运行
p.then(()=>{},()=>{})
// const p1 = new MyPromise((resolve, reject)=>{
// resolve(123)
// })
// // 用户不一定 一定传函数 所以callback也不一定是函数
// // 回忆promise知识
// // 如果传递的不是一个函数 then函数会有一个 状态穿透 p 是什么状态 then就是什么状态
// const p2 = p1.then(123,undefined)
// setTimeout(()=>{
// // 两个状态是完全一样的
// console.log(p1)
// console.log(p2)
// },0)
// console.log(p1) // p1 是完成的 Promise {1}
// console.log(p2) // p2 是pending 未完成的 因为状态吸收 是放在微队列的
p.then((data)=>{
console.log(data)
},(err)=>{
console.log(err)
})
p.then(()=>{},()=>{})
总结思路:
1:then方法的参数 两个参数 参数1:完成时运行 参数2 :失败时运行
then(onFulfilled , onRejected){}
调取时p.then(()=>{},()=>{})
2:then方法的返回值
会返回一个新的promise
新的promise什么时候完成就调用resolve,什么时候失败就调用reject
then(onFulfilled , onRejected){ // then方法一定会返回一个新的promise return new MyPromise((resolve, reject) => {}
}
3: 核心问题 onFulfilled onRejected resolve reject 这四个东西什么时候执行,不一定是立即执行的
因为不是立即执行的,所以需要用到队列
因为then方法不一定会调用多少次,每调用一次会生成这4个东西,就会形成队列,到时候promise完成了(成功了或者失败了)需要到队列中一个一个取出来去处理
所以 需要一个队列 #thenables
随后把这个4个东西放进这个队列中,以便处理
4:启动队列处理 -- 封装一个私有方法 #run方法
什么时候调用这个run方法?
第一个时间节点就是调then方法的时候
第二个时间节点是调取resolve或reject的时候
5:#run方法
1)首先要判断当前状态如果当前状态是一个pending 就不需要处理直接return
2)反之,如果不是pending 就把队列里面按照先进先出的原则一个一个取出来,然后依次执行
3)结构出来进行处理时,会出现两种情况
当前promise的状态是已完成(fulfilled)
当前promise的状态是异常
因为两者内部执行逻辑差不多,所以可以提取成一个公共函数 #handleCallback
6: 处理handleCallback函数
#handleCallback(callback ,resolve , reject ){}
传递三个参数
1)首先考虑这个callback一定是个函数吗?
正常情况下,它一定是个函数,但是在一些特殊情况下不一定
首先确定这个callback哪来的?
通过run方法一个一个提取出来的,不是onFulfilled就是onRejected
而run方法提取出来的参数,是then方法像队列中push进去的
this.#thenables.push({ onFulfilled , onRejected , resolve , reject , });
then方法这两个参数 是用户传进来的
p.then(()=>{},()=>{}) OR p.then(123,undefined)
所以 需要先判断一下callback是不是一个函数 如果不是一个函数,就做状态穿透
同时回忆 promise的知识 --- 状态穿透 注释里面有 不做过多解释
2)callback 是一个函数,那么就要执行这个函数
函数的执行也要放在微队列里面,执行这个函数的时候,要把这个结果数据传过去
那么执行这个结果返回的到底是resolve还是reject呢?
需要利用try catch