深入浅出Promise

自 es6 推出promise以来,promise强大的异步处理功能被广大开发者认可并广泛使用,对于promise api更是驾轻就熟。但是在我们面试找工作的时候,难免会遇到一些比较难缠的面试官,可能他们提问的不仅仅是停留在api如何使用的层面上,而是:“你知道其中的实现原理吗?”,“你了解过它的底层原理吗?”,“你自己有尝试去实现过吗?” 。。。

诸如此类的灵魂拷问,而这时候的你是不是也曾像我一样,在那里支支吾吾了半天,也不清楚自己说了些啥,面试官还时不时皱了一下眉头看着你,那场面极为尴尬。

想要解决这场尴尬的场面有以下两种方案:

1.勇敢的对面试官说:“老子不懂” (这是我以前的选择)

2.硬着头皮,也开始了解相关的实现原理 (这种方法能让自己的内功慢慢变得深厚起来)

废话不多说了,下面开始来了解一下promise的原理

Promise的声明:

 

  1. 由于new Promise((resolve, reject)=>{}) ,所以传入的是一个执行器函数(executor),这个函数默认就会立即执行。
  2. executor里面有两个参数,一个叫resolve(成功的函数),一个叫reject(失败的函数)

  那我们就用class 类来声明

  

class Promise {
  constructor (executor) {

    const resolve = () => { 

    }

    const reject = () => {

    }

      executor(resolve, reject);
  }
}

Promise States:

  1.Promise存在三个状态(statependingfulfilledrejected

  2.pending(等待态)为初始态,并可以转化为fulfilled(成功态)和rejected(失败态)

  3.成功时,不可转为其他状态,且必须有一个不可改变的值(value

  4.失败时,不可转为其他状态,且必须有一个不可改变的原因(reason

  5.new Promise((resolve, reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变。

  6.new Promise((resolve, reject)=>{reject(reason)}) reject为失败,接收参数reason,状态改变为rejected,不可再次改变。

  7.若是executor函数报错 直接执行reject();

  1)根据 1 我们先在顶部定义三个状态

const PENDING = 'peding',
      FULFILLED = 'fulfilled',
      REJECTED = 'rejected';

 

  2) 根据 2、3、4、5、6、7往我们的promise填充一些东西

class Promise {
  constructor (executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
      }  
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
      }
    }

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
}

then :

  1. Promise有一个叫做then的方法,里面有可选两个参数:onFulfilled,onRejected,成功有成功的值,失败有失败的原因
  2. 当状态statefulfilled,则执行onFulfilled当状态staterejected,则执行onRejected
const PENDING = 'PENDING',
      FULFILLED = 'FULFILLED',
      REJECTED = 'REJECTED';

class Promise {
  constructor (executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
      }  
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
      }
    }

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then (onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }

    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  }
}

到这里最基本的Promise算是完成了,我们来试一下这几个例子:

 

let promise = new Promise((reslove, reject) => {
  resolve("success")
  // reject("error")
  // throw new Error("Error")
})

promise.then(value => {
  console.log(value) // success  
}, reason => {
  console.log(reason) 
})

 

let promise = new Promise((reslove, reject) => {
  // resolve("success")
  reject("error")
  // throw new Error("Error")
})

promise.then(value => {
  console.log(value) 
}, reason => {
  console.log(reason) // error
})
let promise = new Promise((reslove, reject) => {
  // resolve("success")
  // reject("error")
  throw new Error("Error")
})

promise.then(value => {
  console.log(value) 
}, reason => {
  console.log(reason) // error
})

这几个例子都能按照我们的预期走,并打印相关的信息,再看下面这个setTimeout的例子:

let promise = new Promise((reslove, reject) => {
  // resolve("success")
  // reject("error")
  // throw new Error("Error")
  setTimeout(() => {
    resolve("success")
  }, 2000)
})
//没有打印 success
promise.then(value => {
  console.log(value)  
}, reason => {
  console.log(reason)
})

原本的预期应该是2s 后打印出 success,实际却没有执行,原因是在then方法中状态为pending的时候我们没有做处理,只有状态为fulfilled或者rejected的时候会执行相应的onFulfilled或onRejected,所有我们应该在状态为pending的时候,分别将成功的回调和失败的回调分别收集起来,然后在状态改变的时候再相应的去执行(这里就是用到发布/订阅的原理)。那我们先定义两个容器:onResolveCallbacks,onRejectedCallbacks

//用于收集成功的回调函数
this.onResolveCallbacks = [];
//用于收集失败的回调函数
this.onRejectedCallbacks = [];

在then方法中,当状态pending的时候收集(订阅):

then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }

    if (this.status === REJECTED) {
      onRejected(this.reason);
    }

    if (this.status === PENDING) {
      // 订阅
      this.onResolveCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }

然后我们在状态改变的时候去执行(发布):

const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        // 发布
        this.onResolveCallbacks.forEach((fn) => fn());
      }
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        // 发布
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    }

然后我们在试一下原先的例子:

let promise = new Promise((reslove, reject) => {
  // resolve("success")
  // reject("error")
  // throw new Error("Error")
  setTimeout(() => {
    resolve("success")
  }, 3000)
})

promise.then(value => {
  console.log(value)  //2s后  打印了success
}, reason => {
  console.log(reason)
})

这次能如我们所愿了,那么接下来要考虑的就是经典的链式调用了

链式调用:

  在此之前我们先来看看几个例子:

  1. 通过return 传递结果, 也就是说可以通过return将上一个then 的结果传递给下一个

let promise = new Promise((reslove, reject) => {
  resolve("success")
})
promise.then(value => {
  return value
}, reason => {
  console.log(reason)
})
.then(value => {
  console.log(value)  //success
})

  2.通过新的promise resolve 结果

promise.then(value => {
  return value
}, reason => {
  console.log(reason)
})
.then(value => {
  return new Promise((reslove, reject) => {
    resolve("success")
  })
})
.then(value => {
  console.log(value) //success
})
  3.通过新的promise setTimeout异步 resolve 结果
promise.then(value => {
  return value
}, reason => {
  console.log(reason)
})
.then(value => {
  return new Promise((reslove, reject) => {
    setTimeout(() => {
      resolve("success") 
    },2000) 
  })
})
.then(value => {
  console.log(value) //success
})

  4.通过新的promise reject 结果

promise.then(value => {
  return value
}, reason => {
  console.log(reason)
})
.then(value => {
  return new Promise((reslove, reject) => {
    setTimeout(() => {
      reject("error") 
    },2000) 
  })
})
.then(value => {
  console.log(value)
}, reason => {
  console.log(reason) //error
})

  5.then 走了失败的回调后  再走then

promise.then(value => {
  return value
}, reason => {
  console.log(reason)
})
.then(value => {
  return new Promise((reslove, reject) => {
    setTimeout(() => {
      reject("error") 
    },2000) 
  })  
})
.then(value => {
  console.log("FulFilled:"+ value)
}, reason => {
  console.log("Rejected:"+ reason) //Rejected: error
  // 默认 return undefined
})
.then(value => {
  console.log("FulFilled:"+ value) //FulFilled: undefined
}, reason => {
  console.log("Rejected:"+ reason) 
})

  6.then 直接 throw new Error

promise.then(value => {
  return value
}, reason => {
  console.log(reason)
})
.then(value => {
  return new Promise((reslove, reject) => {
    setTimeout(() => {
      reject("error") 
    },2000) 
  })  
})
.then(value => {
  console.log("FulFilled:"+ value)
}, reason => {
  console.log("Rejected:"+ reason) //Rejected: error
  // 默认 return undefined
})
.then(value => {
  throw new Error("throw new Error")
})
.then(value => {
  console.log( value)
}, reason => {
  console.log("抛出异常:"+ reason) // 抛出异常: throw new Error
})

  7.用catch 捕获异常 (在本质上就是一个then, 遵循then的运行原则)

promise.then(value => {
  return value
}, reason => {
  console.log(reason)
})
.then(value => {
  return new Promise((reslove, reject) => {
    setTimeout(() => {
      reject("error") 
    },2000) 
  })  
})
.then(value => {
  console.log("FulFilled:"+ value)
}, reason => {
  console.log("Rejected:"+ reason) //Rejected: error
  // 默认 return undefined
})
.then(value => {
  throw new Error("throw new Error")
})
.then(value => {
  console.log( value)
} 
// 走最近的失败的回调函数
// , reason => {
//   console.log("抛出异常:"+ reason) // 抛出异常: throw new Error
// }
)
.catch((err) => {
  console.log("catch: " + err) // atch: throw new Error
  return "catch error"
})
.then(value => {
  console.log("then:"+ value) //then: catch error
})
  8.返回值不能是promise本身
promise.then(value => {
  return promise  //TypeError: Chaining cycle detected for promise #<MyPromise>  循环引用报错
})

总结一下:

  成功的条件是什么?

    1.then return 普通值value

    2.then return 新的promise 成功态的结果 value

  失败的条件是什么

    1.then return 新的promise 失败态的原因 reason

    2.then 抛出异常 throw new Error
  
  promise 怎么链式调用  reuturn this? then 不具备 this  应该是 return new Promise
let promise2 = promise.then(value => {
  //return 第一次返回的新的Promise结果
}).then(value => {
  // 第二次返回的结果
})

let promise2 = promise.then(value => {
  //retun 第一次返回的新的Promise结果
})

// 第一次then返回的新的Promise结果
promise2.then(value => {

})

这两种写法也是不一样的。

了解这些情况以后,参照Promises/A+规范来实现: 

  1、为了达成链式,我们默认在第一个then里返回一个promise。规范中规定了一种方法,就是在then里面返回一个新的promise,称为promise2: promise2 = new Promise((resolve, reject)=>{})

   将这个promise2返回的值传递到下一个then中

   如果返回一个普通的值,则将普通的值传递给下一个then中

  2、当我们在第一个then中 return 了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值

   规范中规定onFulfilled()或onRejected()的值,即第一个then返回的值,叫做x,判断x的函数叫做resolvePromise


  //x 普通值  promise
  then(onFulfilled, onRejected) {

  let promise2 = new Promise((resolve, reject) => {
    if (this.status === FULFILLED) {
      let x = onFulfilled(this.value);
      resolvePromise(promise2, x, resolve, reject);
    }

    if (this.status === REJECTED) {
      let x = onRejected(this.reason);
      resolvePromise(promise2, x, resolve, reject);
    }

    if (this.status === PENDING) {
      // 订阅
      this.onResolveCallbacks.push(() => {
        let x = onFulfilled(this.value);
        resolvePromise(promise2, x, resolve, reject);
      });
      
      this.onRejectedCallbacks.push(() => {
        let x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      });
    }
  });

  return promise2;
}

实现resolvePromise函数:

  1、判断x
  Otherwise, if x is an object or function,Let then be x.then
  x 不能是null
  x 是普通值 直接resolve(x)
  x 是对象或者函数(包括promise), let then = x.then

  2、当x是对象或者函数(默认promise)
  声明了then
  如果取then报错,则走reject()
  如果then是个函数,则用call执行then,第一个参数是this,后面是成功的回调和失败的回调
  如果成功的回调还是pormise,就递归继续解析

  3、成功和失败只能调用一个 所以设定一个called来防止多次调用

function resolvePromise(promise2, x, resolve, reject){
  // 循环引用报错
  if(x === promise2){
    // reject报错
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // 防止多次调用
  let called;
  // x不是null 且x是对象或者函数
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+规定,声明then = x的then方法
      let then = x.then;
      // 如果then是函数,就默认是promise了
      if (typeof then === 'function') { 
        // 就让then执行 第一个参数是this   后面是成功的回调 和 失败的回调
        then.call(x, y => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          // resolve的结果依旧是promise 那就继续解析
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        })
      } else {
        resolve(x); // 直接成功即可
      }
    } catch (e) {
      // 也属于失败
      if (called) return;
      called = true;
      // 取then出错了那就不要在继续执行了
      reject(e); 
    }
  } else {
    resolve(x);
  }
}

但是到这里还会有问题:

// x 普通值  promise
then(onFulfilled, onRejected) {
  // 在下面的这段代码没执行完之前 resolvePromise 能拿到promise2吗? 很显然是不能的
  let promise2 = new Promise((resolve, reject) => {
    if (this.status === FULFILLED) {
      let x = onFulfilled(this.value);
      // 拿不到promise2
      resolvePromise(promise2, x, resolve, reject);
    }

    if (this.status === REJECTED) {
      let x = onRejected(this.reason);
      resolvePromise(promise2, x, resolve, reject);
    }

    if (this.status === PENDING) {
      // 订阅
      this.onResolveCallbacks.push(() => {
        let x = onFulfilled(this.value);
        resolvePromise(promise2, x, resolve, reject);
      });

      this.onRejectedCallbacks.push(() => {
        let x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      });
    }
  });

  return promise2;
}

那我们用 setTimeout 异步执行是不是就可以解决了,结合一下Promises/A+ 规范:

  1、Promises/A+规范规定onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略
  onFulfilled返回一个普通的值,成功时直接等于 value => value
  onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误 reason => throw err

  2、Promises/A+规范规定onFulfilled或onRejected不能同步被调  用,必须异步调用。我们就用setTimeout解决异步问题
  如果onFulfilled或onRejected报错,则直接返回reject()

// x 普通值  promise
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === PENDING) {
        // 订阅
        this.onResolveCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }

这些终于舒服了

   catch 方法:

catch (errorCallback) {
    return this.then(null, errorCallback);
  }

  resolve 方法:

Promise.resolve = function(val){
  return new Promise((resolve,reject)=>{
    resolve(val)
  });
}

  reject 方法:

Promise.reject = function(val){
  return new Promise((resolve,reject)=>{
    reject(val)
  });
}

  race 方法:

Promise.race = function(promises){
  return new Promise((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(resolve,reject)
    };
  })
}

  all 方法:获取所有的promise,都执行then,把结果放到数组,一起返回

Promise.all = function(promises){
    let arr = [];
    let i = 0;
    function processData(index,data){
      arr[index] = data;
      i++;
      if(i == promises.length){
        resolve(arr);
      };
    };
    return new Promise((resolve,reject)=>{
      for(let i=0;i<promises.length;i++){
        promises[i].then(data=>{
          processData(i,data);
        },reject);
      };
    });

  finally 方法:

Promise.prototype.finally = function(onFinished) {
  return this.then(val => {
    onFinished()
    return val
  }).catch((err) => {
    onFinished()
    return err
  })
}

  any 方法:

  跟 Promise.all 正好相反,只要有一个是 promise 是 fulfilled 时,则直接返回该结果,如果都是 rejected ,则报错 。 

Promise
  .any([
    Promise.reject('rejected'),
    Promise.resolve('fulfilled')
 ])
 .then(res => console.log(res))
 .catch(err => console.error(err));
// fulfilled

Promise
  .any([
    Promise.reject('rejected1'),
    Promise.reject('rejected2')
 ])
 .then(res => console.log(res))
 .catch(err => console.error(err));
// AggregateError: All promises were rejected

Promise
  .any([
    Promise.resolve('resolve1'),
    Promise.resolve('resolve1')
 ])
 .then(res => console.log(res))
 .catch(err => console.error(err));
// resolve1

 

 

posted @ 2021-03-16 11:47  LazyHuang  阅读(104)  评论(2)    收藏  举报