手写一个符合Promise/A+规范的Promise

2019-10-14  本文已影响0人  成熟稳重的李先生

Promise的基本概念:

Promise使异步脱离了回调地狱,可以更优雅的操作异步函数的执行结果。究其根本,不过是使用了发布/订阅来达到这一效果。

function Promise(executor){  // executor是用户要执行的异步方法(立即执行)
  this.status = "pending";  // 初始状态
  this.value = undefined;  //成功的值
  this.reason = undefined;  // 失败的值(原因)
  this.onResolvedCallbacks = [];  // “成功”的订阅者
  this.onRejectedCallbacks = [];  // “失败”的订阅者
  let self = this;
  function resolve(value){  // resolve/reject这两个方法,都是用户主动调用的
    self.status = "resolved";  // 改变promise状态  
    self.value = value;   //成功的值
    self.onResolvedCallbacks.forEach(fn => fn()); // 发布这个“成功”的消息
  }
  function reject(reason){
    self.status = "rejected"; 
    self.reason = reason;  // 失败的原因
    self.onRejectedCallbacks.forEach(fn => fn()); // 发布这个“失败”的消息
  }
  try {
    executor(resolve, reject);  // 立即执行用户的异步方法,并且将这两个出发函数传递给用户
  }catch(e){
    reject(e); // 异常也视为失败
  }
}
Promise.prototype.then = function(onFulfilled, onRejected){
  onFulfilled = (typeof onFulfilled === "function") ? onFulfilled : val => val;  // promise有向下传递的特性,如果不处理,直接将值传递给下一层
  onRejected = (typeof onRejected === "function") ? onRejected : reason => {
    throw reason;
  };
  let self = this;
  let promise2 = new Promise((resolve, reject) => {  // 这就是为什么可以一直then(因为then返回的还是一个promise)
    if(self.status === "resolved"){  // 如果这个异步已经成功,那么直接调用
      setTimeout(() => { //这里使用setTimeout是,因为在下边代码中要使用promise2,如果是同步的话,就拿不到promise2,因此需要将其放入下一个队列中
        try {
          let x = onFulfilled(self.value); // 执行成功的函数(有可能它还返回promise,因此如下有函数“resolvePromise”)
          resolvePromise(promise2, x, resolve, reject); //处理这个结果
        }catch(e){
          reject(e); // 如果函数执行失败,那么会走向下一个then的失败状态
        }
      }, 0)
    }else if(self.status === "rejected"){
      setTimeout(() => {
        try {
          let x = onRejected(self.reason);
          resolvePromise(promise2, x, resolve, reject);
        }catch(e){
          reject(e);
        }
      }, 0)
    }else{
      self.onResolvedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(self.value);
            resolvePromise(promise2, x, resolve, reject);
          }catch(e){
            reject(e)
          }
        }, 0)
      });
      self.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(self.reason);
            resolvePromise(promise2, x, resolve, reject);
          }catch(e){
            reject(e);
          }
        }, 0)
      })
    }
  })
  return promise2;
}

function resolvePromise(promise2, x, resolve, reject) {
  if(promise2 === x){
    return reject(new TypeError("循环引用了"));
  }
  let called = false;
  if(x !== null && (typeof x === "object" || typeof x === "function")){  // 判断x是否为简单类型值,是则直接resolve,否则继续
    try {
      let then = x.then;
      if(typeof then === "function"){ // 判断其是否有then
        then.call(x, y => {
          if(called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject); // 继续判断这个结果,直到其返回一个简单(非promise)型的值
        }, r => {
          if(called) return;
          called = true;
          reject(r);
        })
      }else{
        //当前then是一个普通对象,直接resolve
        if(called) return;
        called = true;
        resolve(x);
      }
    }catch(e){
      if(called) return;
      called = true;
      reject(e)
    }
  }else{
    if(called) return;
    called = true;
    resolve(x);
  }
}

Promise.defer = Promise.deferred = function(){  //
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  })
  return dfd;
}


module.exports = Promise;

如何知道是否符合规范呢?
请查看文档 Promise/A+
同时,还可以使用包“promises-aplus-tests”来测试你的promise是否符合规范。
使用方式:

yarn add promises-aplus-tests -g
//在你的工作目录下
promises-aplus-tests  xxx.js  //(共872条测试用例)
上一篇下一篇

猜你喜欢

热点阅读