ES6 Promise详解及源码实现

一.Promise是什么?

Promise是一个构造函数,其实例用于描述异步操作完成的状态和结果。

二.Promise有什么用?

根据上一条,我们可以看出:Promise主要用来处理异步操作(如:接口请求,含setTimeout的操作等) 。

那它和我们之前处理异步任务的方式有什么不同呢?

我们先来看看之前咱们处理异步操作的方式:

//成功回调
function successFunc(res) {
    //todo
  console.log(res)
}
// 失败回调
function failFunc(err) {
    //todo
}


//异步操作处理函数
function asyncFunc(successFunc, failFunc) {
  var a = 1;
    setTimeout(function (){
    if(true) {
      successFunc(a)
    }else {
        failFunc('错误信息')
    }
    },5000)
}
//执行异步操作(成功和失败的回调函数时以参数传进去的)
asyncFunc(successFunc, failFunc);

 

    通过上述代码我们可以看出:异步操作的成功和失败的处理函数是提前定义好,然后以参数的形式传入的,当异步操作结果确定之后,根据异步操作的结果调用对应的回调函数。

假如异步操作的回调函数中还有异步任务,就会出现回调中再出现异步操作,异步操作中又有自己的回调,如果这样的嵌套操作过多,就出现了我们常说的回调地狱。代码的可读性和可维护性将大大降低。

 

咱们再来看看如何用Promise来处理异步操作的

new Promise((resolve,reject) => {
    var a = 1;
  setTimeout(function(){
    if(true) {
      resolve(a)
    }else {
      reject('错误信息')
    }
  },1000)
}).then(res=> {
    // todo
  console.log(res)
}).catch(err => {
    // todo
});

 

通过上述代码可以看出:我们是用同步思维方式编写异步操作的代码。

假如成功处理函数中还有异步操作,使用Promsie实现代码如下:

new Promise((resolve,reject) => {
    var a = 1;
  setTimeout(function(){
    if(true) {
      resolve(a)
    }else {
      reject('错误信息')
    }
  },1000)
}).then(res=> {
    // todo
  return new Promise((resolve,reject) => {
    setTimeout(function(){
      resolve(2)
  },1000) // 该异步操作将动态插入调用链中,所以他的成功处理函数时后边的then
  }) 
}).then(res=> {
    console.log(res) // 2
}).catch(err => {
    // todo
});

 

三.Promise中经常使用的点

1. Promise实例化对象有3个状态:pending(待定)、fulfilled(已完成)、rejected(已拒绝)

// -------------pending---------------
console.log(new Promise((resolve,reject) =>{
    resolve(1)
}).then(res => {
    console.log(res)
    return res;
})); // Promise{<pending>}

//----------fulfilled-----------------
var a = new Promise((resolve,reject) => {
    resolve(1);
})
console.log(a) // Promsise {<fulfilled>: 1}

//------------rejected-----------------
var a = new Promise((resolve,reject) => {
    reject(1);
})
console.log(a) // Promsise {<reject>: 1}

 

 

2. then、catch

var a = new Promise((resolve,reject) => {
  // 异步操作
  setTimeout(()=> {
    resolve(1);
  },1000)
    
}).then(res => {
    console.log(1)
  return 5; // then中函数的return出去的值(Promise实例除外)将作为Promise实例对象的值,同时也会传到下个then里边的函数中
}).catch(e => {
    // todo
}).then(res => {
    console.log(res); // 5
});

//------------或者将catch中的函数放在then的第二个参数中-------------
var a = new Promise((resolve,reject) => {
    resolve(1);
}).then(res => {
    console.log(1)
}, e => {
    // todo
});

 

3. 静态方法:Promise.resolve(x)

作用:返回一个状态为fulfilled,值为x的Promise实例

Promise.resolve(1); // // Promsise {<fulfilled>: 1}

//----------对状态为fulfilled的Promise实例,也可以调用then
var a = Promise.resolve(1); 
a.then(res => {
    console.log(res); // 1
  return 2;
});

 

4. 静态方法:Promise.reject(x)

作用:返回一个状态为rejected,值为x的Promise实例

Promise.reject(1); // // Promsise {<rejected>: 1}

//----------对状态为rejected的Promise实例,也可以调用catch
var a = Promise.reject(1); 
a.then(res => {
}).catch(e => {
    console.log(e) // 1
});

 

5. 静态方法:Promise.all(iterable)

将多个Promise实例包装到一起,当iterable中所有Promise对象状态都为fulfilled时,则执行Promsie.all()后的then中的函数,值为每项的结果的集合。 一旦有其中某项状态为rejected时,则立即执行Promsie.all()后的catch中的函数

//-----------fulfilled----------
var promise1 = new Promise((resolve,reject) => {
    setTimeout(()=> {
        resolve(1);
    },1000)
}) 
var promise2 = new Promise((resolve,reject) => {
    setTimeout(()=> {
        resolve(2);
    },2000)
}) 
Promise.all([promise1 ,promise2]).then(res => {
    console.log(res) // 2s之后打印[1,2]
})

//-----------rejected----------
var promise1 = new Promise((resolve,reject) => {
    setTimeout(()=> {
        reject(1);
    },1000)
}) 
var promise2 = new Promise((resolve,reject) => {
    setTimeout(()=> {
        resolve(2);
    },2000)
}) 
Promise.all([promise1 ,promise2]).then(res => {
    
}).catch(e => {
    console.log(e); // 1s后打印1
})

 

6. 静态方法:Promise.any(iterable)(还在草案阶段,浏览器暂不支持)

将多个Promise实例包装到一起,当iterable中任意一个Promise对象状态变为fulfilled时,则执行Promsie.any()后的then中的函数,值为那一项的值。当iterable中所有的Promise对象状态变为rejected时

 

7. Promise.race(iterable)

将多个Promise实例包装到一起,取iterable中最先改变状态Promise对象,若状态为fulfilled则执行Promsie.race()后的then中的函数,值为那一项的值。若状态为rejected则执行Promsie.race()后的catch中的函数,值为那一项的值。

8. finally

上一个操作完成,无论是resolve还是reject,finally中的函数都会执行。注意:finally中的函数只处理逻辑,不影响Promise实例本身状态和值

new Promise((resolve,reject)=>{
    resolve(1)
}).finally(() => {
    return 2
}).then(res => {
    console.log(res) // 1
})

 

 

四.Promise中重难点和易忽略点

  • resolve和then中的返回值若是Promise对象时,该Promise对象动态插入链式调用中。eg:
new Promise((resolve,reject)=>{
    resolve(Promise.resolve(1))
}).then(res => {
    return Promise.resolve(1).then(res => {
        console.log(1)
    }).then(res => {
        console.log(2)
    });
}).then(res => {
    console.log(3)
}) // 1   2   3

 

 

  • then()中参数不是函数时,链式调用就直接进行下个环节的操作。(源码参见Promise then
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(res => {
        console.log(res) // 1
  }) 

 

 

  • catch语句本质是捕获程序中的错误(reject会转换成程序错误),程序报错未被捕获时,会一直向后传递,直到捕获为止
//---------------捕获reject--------------
new Promise((resolve,reject)=>{
    reject(1)
}).then(res => {
}).catch(e=>{
    console.log(e) // 1
})

//---------------捕获代码问题--------------
new Promise((resolve,reject)=>{
    console.log(lalaa)
    resolve(1)
}).then(res => {
}).catch(e=>{
    console.log(e) //ReferenceError: lalaa is not defined...
})


// -----------最后一个catch可以捕获之前的所有错误------------------------
new Promise((resolve,reject)=>{ reject(1) }).then(res => { }).then(res => { }).then(res => { }).then(res => { }).catch(e=>{ console.log(e) // 1 })

 


  • resolve()后的代码会按同步方式执行,then中传递的函数return **之后代码不执行
new Promise((resolve, reject) => {
    resolve();
    console.log(111);
})
.then(() => {
    consle.log(222);
    return 3;
    consle.log(333);
}) // 111   222

 

五.自定义实现Promise—then

  • 用类实现源码
class YelPromise {
  //构造函数主体
  constructor (mainFunc) {
  // 属性初始化
    Object.assign(this,{
      status: "pending",// YelPromise的执行状态
      value : undefined,// YelPromise的值
      successArr : [], // 成功回调数组(then中的函数组成的队列)
    })
    mainFunc(this.resolve.bind(this));
  }

  // 原型上的resolve方法
  resolve (resolveValue){
    let doNextfunc = function (self,nextValue) {
      // 使用异步任务的原因:调用resolve方法时,还没执行then函数,successArr为空
      // 注意:这个地方使用的setTimeout来模拟实现,在Promise中实际使用的是微任务的异步方式
      setTimeout(() =>{
        let isYelPromise = function (val){
          // 若返回值为YelPromise实例,则将该实例动态插入执行队列中
          if(val instanceof YelPromise) {
            self.successArr.unshift(...val.successArr);
            val.successArr = [];
            // 使用setTimeout是为了取到val.value
            setTimeout(()=>{
              self.resolve(val.value);
            },0)
            return self;
          } else {
            return false;
          }
        };
        if(isYelPromise(nextValue)) {
          return;
        }
        // 先进先出原则,取出第一个成功回调函数并执行
        let successHeadFunc = self.successArr.shift();
        if(successHeadFunc) {
          let result = successHeadFunc(nextValue);
          if(isYelPromise(result)) {
            return;
          }
          // 根据执行结果,更改promise实例的value值(真实的Promise中的then会
          // 判断传入的参数是否为函数,如果不是,则跳过,实例的value不变)
          if(self.successArr && self.successArr.length){
            self.value = result;
            doNextfunc(self,result) // 继续处理下个then中的函数
          } else {
            returnResult (self,result)
          }
        } else {
          returnResult (self,nextValue)
        }
      },0)
    };
    // 处理完成,更改状态
    let returnResult = function (self,finallyValue){
      self.status  = 'fulfilled';
      self.value = finallyValue;
    }
    // 执行then中传递的函数
    doNextfunc(this,resolveValue);
    return this; // 返回当前对象,方便链式调用
  }
  
  // 原型上的then方法
  then (func){
    // 将then中传递的方法放入successArr,后续按先进先出的原则调用
    this.successArr.push(func);
    // 结果已确定,程序不会再从successArr中取函数执行,故需要手动调用resovle方法
    if(this.status == 'fulfilled') {
      this.resolve(this.value)
    }
    return this;  // 方便链式调用
  }
}

 

  • 用构造函数实现源码
// resolve方法
YelPromise.prototype.resolve = function(resolveValue) {
let doNextfunc = function (self,nextValue) {
      // 使用异步任务的原因:调用resolve方法时,还没执行then函数,successArr为空
      // 注意:这个地方使用的setTimeout来模拟实现,在Promise中实际使用的是微任务的异步方式
      setTimeout(() =>{
        let isYelPromise = function (val){
          // 若返回值为YelPromise实例,则将该实例动态插入执行队列中
          if(val instanceof YelPromise) {
            self.successArr.unshift(...val.successArr);
            val.successArr = [];
            // 使用setTimeout是为了取到val.value
            setTimeout(()=>{
              self.resolve(val.value);
            },0)
            return self;
          } else {
            return false;
          }
        };
        if(isYelPromise(nextValue)) {
          return;
        }
        // 先进先出原则,取出第一个成功回调函数并执行
        let successHeadFunc = self.successArr.shift();
        if(successHeadFunc) {
          let result = successHeadFunc(nextValue);
          if(isYelPromise(result)) {
            return;
          }
          // 根据执行结果,更改promise实例的value值(真实的Promise中的then会
          // 判断传入的参数是否为函数,如果不是,则跳过,实例的value不变)
          if(self.successArr && self.successArr.length){
            self.value = result;
            doNextfunc(self,result) // 继续处理下个then中的函数
          } else {
            returnResult (self,result)
          }
        } else {
          returnResult (self,nextValue)
        }
      },0)
    };
    // 处理完成,更改状态
    let returnResult = function (self,finallyValue){
      self.status  = 'fulfilled';
      self.value = finallyValue;
    }
    // 执行then中传递的函数
    doNextfunc(this,resolveValue);
    return this; // 返回当前对象,方便链式调用
};

// then方法
YelPromise.prototype.then = function(func) {
  // 将then中传递的方法放入successArr,后续按先进先出的原则调用
  this.successArr.push(func);
  // 结果已确定,程序不会再从successArr中取函数执行,故需要手动调用resovle方法
  if(this.status == 'fulfilled') {
    this.resolve(this.value)
  }
  return this;  // 方便链式调用
};

//构造函数主体
function YelPromise(mainFunc) {
  // 属性初始化
  Object.assign(this,{
    status: "pending",// YelPromise的执行状态
    value : undefined,// YelPromise的值
    successArr : [], // 成功回调数组(then中的函数组成的队列)
  })
  mainFunc(this.resolve.bind(this), this.resolve.bind(this));
}

 

  • 测试代码:
var a = new YelPromise((resolve,reject)=> {
    resolve(1)
}).then(res=> {
    return new YelPromise((resolve,reject)=> {
    resolve()
}).then(res=> {
    console.log(1)
}).then(res=> {
    console.log(2)
})
}).then(res=>{
    console.log(3)
}).then(res=>{
    console.log(4)
}) 

 

//---------------捕获代码问题----
posted @ 2020-12-30 20:35  鳯訡  阅读(325)  评论(0编辑  收藏  举报