美文网首页小程序起步
Promise实现及理解

Promise实现及理解

作者: 小本YuDL | 来源:发表于2020-06-16 15:24 被阅读0次

    别让平淡的生活,毁了对未来的向往❤


    1.promise的基本结构

    promise必须接受一个函数( handle ) 作为参数,这个函数有两个参数:resolve,reject,且这两个参数本身也是函数。

    let promise = new Promise((resolve, reject) => {
        resolve('success');  //reject('fail')
    });
    promise.then((res)=>{
      console.log(res); // 输出:success
    },(err)=>{
      console.log(err); // 上面如果执行reject('fail'),这里就输出:fail
    });
    

    2.promise的状态和值

    (1)promise有三个状态
    • Pending( 进行中 )
    • Fulfilled( 已成功 )

    • Rejected ( 已失败 )

    状态只能由 **Pending--> Fulfilled** 或着 **Pending --> Rejected** 两种状态变更,变更之后状态**锁定**了,就不会再变更了。
    
    (2)promise返回两种状态
    • resolve(成功 ):将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)

    • reject(失败):将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)

    resolve 和 reject 函数都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值。

    (3)promise的值

    Promise的值是指状态改变时传递给回调函数的值。

    promise的参数handle函数,包含 resolve 和 reject 两个参数,它们是两个函数,可以用于改变 Promise 的状态和传入 Promise 的值。

    (a) . resolve 传入的 ‘success’ ,就是 promise 的值。

    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('success');
      }, 1000)
    });
    

    (b). promise的 then方法返回一个promise。

    let promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve()
      }, 1000)
    })
    promise2 = promise1.then(res => {
        return new Promise((resolve,reject)=>{ 
            resolve('hello world'); 
        })
    })
    promise2.then(res => {
      console.log(res) //1秒后打印出:hello world
    });
    
    (4)实现一个Promise类
    • 执行状态: status ;可以有 pending,resolved,rejected三个状态。

    • 执行结果: value;返回成功时,传递进来的值由value保存。

    • 失败原因: reason;返回失败时,失败的原因由reason保存。

    • 两个方法: resolve方法,reject方法; 来进行状态值得改变及成功或失败原因的保存。

    • 一个方法: then方法;根据不同状态,执行不同的函数,返回不同的结果。

    • 错误处理: try-catch;能够捕获错误,并且处理错误。

    //创建一个Promise类
    class Promise { 
        constructor(executor) {
            this.status = 'pending'; // 执行状态: 初始默认状态为pending(只有状态为pending才能转换状态)
            this.value = undefined;  // 执行结果:默认赋值为undefined
            this.reason = undefined; // 失败原因:默认赋值为undefined
            let resolve = (value) => {
                if (this.status === 'pending') { 
                    this.value = value; //将传递进来的的值赋给value保存
                    this.status = 'resolved'; //将状态设置成resolved
                }
            }
            let reject = (reason) => {
                if (this.status === 'pending') {
                    this.reason = reason; //将传递进来的失败原因赋给reason保存
                    this.status = 'rejected'; //将状态设置成rejected
                }
            }
            // 当代码出现错误的情况下,我们需要能够捕获错误,并且处理错误
            try{ 
                executor(resolve, reject); //默认执行executor
            }catch(e){
                reject(e);//如果发生错误,将错误放入reject中
            }
        }
        then(onFulfilled, onRejected) {  //等同于es5的Promise.prototype.then 当调用then的时候,根据状态,来执行不同的函数
            if (this.status === 'resolved') { 
                onFulfilled(this.value); //如果状态是resolved,执行成功的resolve,并将成功后的值传递过去
            }
            if (this.status === 'rejected') {
                onRejected(this.reason); //如果状态是rejected,执行失败的reject,并将失败原因传递过去
            }
        }
    }
    

    以上实现会有一个问题,如果promise内部使用setTimeout去调用回调函数时,我们会发现上面的代码在控制台完全没有反应,实际上是因为我们没有处理这种延迟调用的情况。

    (5)setTimeout 调用问题
    • 成功函数数组: successStore[ ];成功resolve状态时,依次调用数组中函数。

    • 失败函数数组: failStore[ ];失败reject状态时,依次调用数组中函数。

    • then方法:新增pending状态的判断,成功的函数存放到successStore数组里,失败的函数存放到fillStore数组中。

    let promise = new Promise((resolve, reject) => {
        setTimeout(() =>{
            resolve('success');  //reject('fail')
      },1000);
    });
    promise.then((res)=>{
      console.log(res);
    },(err)=>{
      console.log(err);
    });
    

    解决方法:【在上述代码基础上完善,如下代码】

    class Promise { 
        constructor(executor) {
            ...此处略去部分代码
            this.successStore = [];  //定义一个存放成功函数的数组
            this.failStore = [];  //定义一个存放失败函数的数组
            let resolve = (value) => {
                if (this.status === 'pending') { 
                    ...此处略去部分代码
                    this.successStore.forEach(fn => fn()); //依次执行数组中的成功函数
                }
            }
            let reject = (reason) => {
                if (this.status === 'pending') { 
                    ...此处略去部分代码
                    this.failStore.forEach(fn => fn()) //依次执行数组中的失败函数
                }
            }
           ...此处略去部分代码
        }
        then(onFulfilled, onRejected) { //等同于es5的Promise.prototype.then 当调用then的时候,根据状态,来执行不同的函数
            ...此处略去部分代码
            if (this.status === 'pending') { //此处增加一种状态判断
                this.successStore.push(() => { //当状态为pending时将成功的函数存放到数组里
                    onFulfilled(this.value);
                })
                this.failStore.push(() => { //当状态为pending时将失败的函数存放到数组中
                    onRejected(this.reason);
                })
            }
        }
    }
    

    3.promise的then方法详解

    Promise 对象的 then 方法接受两个参数:

    promise.then(onFulfilled, onRejected);
    
    (1)参数可选
    • 如果参数不会函数,则忽略

    • 如果参数为函数:onFulfilled在promise状态变为成功时调用,onRejected在promise状态变为失败时调用。

    (2)多次调用
    • 当 promise 成功状态时,所有 onFulfilled 需按照其注册顺序依次回调。

    • 当 promise 失败状态时,所有 onRejected 需按照其注册顺序依次回调。

    (3)返回
    • then 方法必须返回一个新的 promise 对象
    promise2 = promise1.then(onFulfilled, onRejected);
    
    • then 方法可以链式调用
    promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
    
    (4)链式调用
    a. 在then方法的回调里返回一个普通值。

    无论是成功还是失败的回调,都会进入到下一个then()的成功态里。

    let promise = new Promise((resolve, reject) => {
        resolve('success');
    });
    //返回一个普通值
    promise.then(res => {  
        console.log(res); //success
      return "hello world";
    }, err => {
        console.log(err); 
    }).then(res => {
        console.log(res); //hello world
    }, err => {
        console.log(err); 
    })
    
    b.在then方法的回调里返回一个新的Promise
    let promise = new Promise((resolve, reject) => {
        resolve();
    });
    //返回一个新的Promise
    promise.then((res)=>{  
        return new Promise((resolve,reject)=>{ 
            resolve('hello world'); 
        })
    },(err)=>{
        console.log(err); 
    }).then((res)=>{
        console.log(res); //hello world    
    },(err)=>{
        console.log(err);
    })
    

    4.promise的其他方法的实现

    (1)promise.catch 方法

    相当于调用 then 方法, 但只传入 Rejected 状态的回调函数

    catch (onRejected) {
        return this.then(undefined, onRejected);
    }
    
    (2)promise.reject 方法
    static resolve (value) {
      return new Promise(resolve => reject(value));
    }
    
    (3)promise.resolve方法
    static resolve (value) {
      return new Promise(resolve => resolve(value));
    }
    
    (4)promise.all 方法
    • 【经典的手写promise.all 的面试题】

    • 返回一个Promise对象

    • 遍历传入的list集合; Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组

    • 数组参数如果不是Promise实例,先调用Promise.resolve

    • 所有状态都变成 fulfilled 时返回的Promise状态就变成 fulfilled

    • 有一个rejected时返回的Promise 状态就变成 rejected

    static all(list){
        return new Promise((resolve, reject) => {
        let values = [];
        let count = 0;
       for (let [i, p] of list.entries()) {
          this.resolve(p).then(res => {
            values[i] = res;
            count++;
            // 所有状态都变成fulfilled时,就resolve
            if (count === list.length) {
                resolve(values);
            }
          }, err => {
            // 有一个被rejected时,就rejected
            reject(err);
          })
        }
      })
    }
    
    
    (5)promise.race 方法
    • 只要有一个实例率先改变状态,新的Promise的状态就跟着改变
    static race (list) {
      return new MyPromise((resolve, reject) => {
        for (let p of list) {
         //有一个状态改变时,就rejected或resolve
          this.resolve(p).then(res => {
            resolve(res)
          }, err => {
            reject(err)
          })
        }
      })
    }
    
    (6)promise.finally 方法
    • finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
    finally (cb) {
      return this.then(
        value  => Promise.resolve(cb()).then(() => value),
        reason => Promise.resolve(cb()).then(() => { throw reason })
      );
    };
    
    

    5.promise其他方法的使用

    (1)Promise.all( )
    • 传入的参数是一个数组

    • 若全部都是成功的回调,则执行会输出返回值组成的数组 【传入p1,p2成功的回调】

    //成功
    let p1 = new Promise((resolve,reject) => {
      let value = {
            code:0,
            msg:'one success'
        };
      resolve(value);
    });
    //成功
    let p2 = new Promise((resolve,reject) => {
      let value = {
            code:1,
          msg:'two success'
      };
      resolve(value);
    });
    //失败
    let p3 = new Promise((resolve,reject) => {
      let reason = 'all error';
      reject(reason);
    });
    
    Promise.all([p1,p2]).then((val) => {
      console.log(val);
    }).catch(err=>{
      console.log(err);
    });
    

    输出结果:

    输出结果
    • 若存在失败的回调,则直接返回失败的信息。【加入P3失败的回调】
    Promise.all([p1,p2,p3]).then(val => {
      console.log(val);
    }).catch(err=>{
      console.log(err);
    });
    

    输出结果:

    输出结果

    【适合场景】

    如上p1,p2,p3的三个情况,只有全部是成功的回调promise才会成功状态,若有一条失败则promise返回状态。

    • 比如现在有一个表单页,里面有若干多个表单项,只有在所有表单都通过校验才允许用户进行下一步操作。
    • 每一个业务环节需要顺序执行,执行顺序不可变,状态不可逆,上一个执行结果可能需要作为下一步的输入。
    (2)Promise.race( )
    • 传入的参数是一个数组

    • 会返回最先出结果的那个元素的状态

    //成功回调,直接返回
    let p1 = new Promise((resolve,reject) => {
      let value = {
        code:0,
        msg:'one success'
      };
      resolve(value);
    });
    
    //成功状态,延迟1秒返回
    let p2 = new Promise((resolve,reject) => {
      let value = {
        code:1,
        msg:'two success'
      };
     setTimeout(()=>{
         resolve(value);
      },1000);
    });
    
    //失败状态,延迟0秒返回   
    let p3 = new Promise((resolve,reject) => {
      let reason = 'race error'
      setTimeout(()=>{
         reject(reason);
      },0);
    });
    
    Promise.race([p1,p2,p3]).then(val => {
      console.log(val);
    }).catch(err=>{
      console.log(err);
    })
    

    输出结果:
    为什么不输出延迟0秒的p3 ‘race error’ 呢,因为这个牵扯到事件执行机制,同步任务先执行,异步setTimeout任务挂起,同步任务执行结束之后再执行异步。所以p3没有p1返回的快。

    输出结果

    【适用场景】

    如上p1,p2,p3,不管他们的状态是成功还是失败,只是返回最先执行完的那个。所以promise.race可以用于快捷地测试接口反应速度。


    参考:https://juejin.im/post/5afd2ff26fb9a07aaa11786c

    相关文章

      网友评论

        本文标题:Promise实现及理解

        本文链接:https://www.haomeiwen.com/subject/hjtrxktx.html