了解promise 其二:.then方法

为了更好的理解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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值