搞懂Promise原理

学习准备:

非常适合初学

  • js同步异步知识
  • 类的了解
  • 原型相关知识
  • 对promise有相关的使用        

先看看基础用法

成功语法格式:

    function test() {
        return new Promise((resolve, reject) => {
            resolve(123)
        })
    }
    test().then(v =>console.log("成功:",v))

 失败语法格式:

    function test() {
        return new Promise((resolve, reject) => {
            reject('error')
        })
    }
    test().then(v =>console.log("成功:",v),r=>console.log('失败:',r))

或者:

    function test() {
        return new Promise((resolve, reject) => {
            reject('error')
        })
    }
    test().catch(r=>console.log('失败:',r))

  •  可以看到失败处理函数的两种写法的结果是相同的
  • 失败和成功 最简单的两种使用方法就是以上内容
  • 下面开始一步步深入


我们先初步写出我们的promise

平时实例化的时候我们是这样的:

  • 传参,然后在内部接收参数
  • constructor  负责处理实例化时传递过来的参数

        class Person {
            constructor(Pname){
                    this.name = Pname;
            }
        }
        let res = new Person('tom');
        console.log(res)

 


  • 下面的 MyPromise  是我们自己写的类
  • 先看看我们怎么使用
  • 实例化 然后传参过去

    function run() {
        return new MyPromise((resolve, reject) => {
            resolve('成功')
        })
    }
    run().then(v => console.log(v))
  • 这里传参我们只是把普通参数 换成了一个函数而已
  • 这下能看清楚吧
  • 注意这里是一个传的是这个 函数
new MyPromise(  (resolve, reject) => {}   )
(resolve, reject) => { }      
  • 注意你传过来的那个函数 它还要接收两个参数  resolve, reject

  • 再看看我们咋写的

  • 它里面调用了 resolvereject,说明这个参数也是一个函数,不然它怎么可能调用呢?

 return new MyPromise((resolve, reject) => {
            resolve('成功')
})
  •  既然传递了参数,那么就要处理一下参数

    class MyPromise {
        constructor(callBack) {

         // callBack 就等同于 (resolve, reject) => { }    
         //callBack() 不就是在调用这个传过来的函数吗
        //(resolve, reject) => { } 不是接收两个参数吗
        //但是这两个参数 它还要拿去调用,所以传两个函数给他

            callBack(this.resolve, this.reject);
        }


        // 在调用他们的时候是不是传了一个值啊,
        //所以在你声明函数的时候接收一下 你调用函数时 传过来的值
        //resolve (成功信息)
        //reject (失败信息)


        resolve = (value) => {

        };

        reject = (value) => {
            
        };
    }

-----------------------------------到这里没有理解的,再消化一下--------------------------------


 分析:这个值怎么就可以出现在这里呢?这里是怎么拿到的呢?

  • 我们是不是写了这么个东西
  • 这样的话 你 调用 resolve(值 ) 或者 reject(值 )  
  • 下面是不是就可以接收到了
  • 下面的函数声明在类里面,那么类里面不就收到了 成功 或者 失败 的传值了吗
        resolve = (value) => {

        };
        reject = (value) => {

        };

那么就再追加一些代码

  • 我们把传过来的值保存起来
  • 为什么要这样存起来呢,下面就知道了。
    class MyPromise {
        constructor(callBack) {
            this.data = null;//用来装 resolve() 或者 reject()调用传过来的值
             callBack(this.resolve, this.reject);
        }
        resolve = (value) => {
            this.data = value;
        };
        reject = (value) => {
            this.data = value;
        };
    }

到此先看看写了个什么玩意

 console.log(new MyPromise((resolve, reject) => {}))

再来看看我们的使用方式

    function run() {
        return new MyPromise((resolve, reject) => {
            resolve('成功')
        })
    }
    run().then(v => console.log(v))

 上面那种方式生疏的,可以看看下面这种写法,差不多一个意思

    let s = new MyPromise((resolve, reject) => {
        resolve('成功')
    })
    s.then(v => console.log(v))
  • 这里是实例化后,在实例化对象中调用 then() 方法
  • 这里把then方法写在外面,也可以写在类里面
  • 这里的this指向实例
  • 通过this.data呢,拿到你 resolve()调用时传过去的参数
    MyPromise.prototype.then = function (resolveCall) {

//resolveCall 就等同于函数 v => console.log(v)
//resolveCall ()不就是在调用这个函数吗

        resolveCall(this.data)
    }

实验一下

    let s = new MyPromise((resolve, reject) => {
        resolve('成功的值')
    })
    s.then(v => console.log(v))

 梳理一下:

  • this.data      就相当于一个公共部分的数据
  • resolve 或 reject 传递一个参数 将这个data  赋值
  • 在  then  方法中取出这个 data  然后调用回调函数 把这个 data 传出去
  • 至此 不就解决问题了

 

-----------------------------------到这里没有理解的,再消化一下--------------------------------


异步问题

  • 我们知道有时候 我们要等到一个ajax请求返回之后我们才回去调用 resolve 或者 reject函数
  • 这样的话,我们任然用上面的方式去使用一下
  • 这里的请求 我就用定时器代替一下
  • 这里的回调函数传参上面讲过了,不再赘述了
    function request(cb) {
        setTimeout(() => {
            cb()
        })
    }
  •  定时器执行的时候就是在执行 resolve 方法
    class MyPromise {
        constructor(callBack) {
            this.data = null;
            callBack(this.resolve, this.reject);
        }
        resolve = (value) => {
            this.data = value;
        };
        reject = (value) => {
            this.data = value;
        };
    }
    MyPromise.prototype.then = function (resolveCall) {
        resolveCall(this.data)
    }
    function run() {
        return new MyPromise((resolve, reject) => {
            request(() => {
                resolve('成功')
            })

        })
    }
    run().then(v => console.log('成功返回的值',v))

可以看到结果并不是我们想要的

为什么会出现这种情况呢?

  • 按照原本的执行顺序是: resolve-->存放值 then-->拿到值然后传递出去
  • 这里由于有一个定时器,开启了异步任务,将  resolve-->存放值  这一个操作放到一边去,暂时不执行
  • 然后执行  then-->拿到值然后传递出去,哎 由于 resolve-->存放值 没有执行的
  • 所以 then-->拿到值然后传递出去  它拿到的值肯定就是初始化的时候的值 null
  • 看一下初始化的操作
 constructor(callBack) {
            this.data = null;
            callBack(this.resolve, this.reject);
        }

那咋整呢?用下面的状态来解决这个问题


了解状态

这里我用 resolved   rejected   去代表两种状态,方便对应相关函数

状态不可逆,一旦改变不可以回到原来状态

  • pendding  初始化状态
  • resolved   成功状态
  • rejected    失败状态

 下面我们将原来的代码改动一下

        constructor(callBack) {
            this.data = null;
            this.state = 'pendding';//初始化状态
            this.resolveList = [];//存放then方法中成功的回调函数
            this.rejectList = [];//存放then方法中失败的回调函数
            callBack(this.resolve, this.reject);
        }
        resolve = (value) => {
            if (this.state !== 'pendding') return;//已经改变的 就不再执行下面
            this.state = 'resolved';//改变状态为自己对应的状态
            this.data = value;
            this.resolveList.length > 0 && this.resolveList.forEach(resolveCall => resolveCall(value))
        };
        reject = (value) => {
            if (this.state !== 'pendding') return;
            this.state = 'rejected';
            this.data = value;
            this.rejectList.length > 0 && this.rejectList.forEach(rejectCall => rejectCall(value))
        };

  •  看下面就知道为什么要这个数组来存放函数了
    MyPromise.prototype.then = function (resolveCall) {

    //如果调用then方法的时候 状态还没有改变,
    //或者说如果调用then方法的时候,resolve还没有执行
    //我们把then方法中的回调函数暂时存起来

        if (this.state === 'pendding') this.resolveList.push(resolveCall)
        else resolveCall(this.data)
        
    }

看下面这种形式,then方法还可以有第二个参数,第二个参数是处理失败的一个回调函数

    function test() {
        return new Promise((resolve, reject) => {
            reject('error')
        })
    }
    test().then(v =>console.log("成功:",v),r=>console.log('失败:',r))

所以改动一下 then 方法

    MyPromise.prototype.then = function (resolveCall,rejectCall) {
        if (this.state === 'pendding') {
            this.resolveList.push(resolveCall);
            this.rejectList.push(rejectCall)
        } 
        else resolveCall(this.data)
    }

梳理一下:

首先这么改动以后为什么就解决了上面的问题呢?

再把前面的问题放出来:

        
resolve = (value) => {
            if (this.state !== 'pendding') return;
            this.data = value;//存值
};   


MyPromise.prototype.then = function (resolveCall,rejectCall) {
       resolveCall(this.data);//将内部存的值,传出去
}

function request(cb) {
        setTimeout(() => {
            cb()
        })
}

function run() {
        return new MyPromise((resolve, reject) => {
            request(() => {
                resolve('失败');//传值到 resolve函数,它收到会存放
            })

        })
}

run().then(v => console.log('成功返回的值', v))
  • 这里结果是 null 不用多说
  • 按照原本的执行顺序是: resolve-->存放值 then-->拿到值然后传递出去

改动之后:

  • state初始化为 pendding
  • 先记住一点,只有调用resolve或者reject状态才会改变
  • resolve-->存放值 被当作异步任务放在一边
  • 执行 then-->拿到值然后传递出去 ,但是发现 状态没变化,还是初始化的状态,也就是还没有执行 resolve或者reject
  • 那就把  then-->拿到值然后传递出去 这个操作给我存起来,等到执行 resolve或者reject 的时候我再执行它
  • 因为等到 执行 resolve或者reject 的时候 值是不是已经传进来了,状态也会发生相应的改变

这样就解决了异步的问题

先写到这里,第一次写不知道有没有叙述清楚,如有不足请指点出来,如果觉得还不错,后面会发后续完善部分

大家的支持是我最大的动力

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值