ES6-Promise 65行代码手撕Promise

555 阅读3分钟

手撕Promise

到了找实习的时候,看面经感觉不是很好,还是写写代码最能记住东西

手撕目标

  • 包含then、catch方法
  • 支持链式调用
  • 异步解决方案

手撕开始

那么首先,如果你不知道Promise,你可能需要绕道先去学习一下,因为这里不想啰嗦要直接动手写滴哦

我这里应该是和网上的很多教程不太一样哈,因为一时没有想到大家的思路......

基本框架

const PENDING = "pending";
const FINISHED = "fulfilled";
const FAILED = "rejected";
class XPromise {
  constructor(handleFinish) {
    let __PromiseState__ = PENDING;
    this.__PromiseState__=__PromiseState__
    this.onResolvedCallbacks = [];
    this.onRejectCallback = undefined;
    this.__PromiseResult__ = undefined;
    const resolve = (val) => {
      ...
    };
    const reject = (val) => {
      ...
    };
    try {
      handleFinish?.(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  then(fun,handleError) {
   ...
  }
  catch(fun) {
    ...
  }
}

解释一下:

  • __PromiseState__我们用这个变量存储Promise对象的状态,上边则预先声明了"pending","fulfilled","rejected"四种状态
  • onResolvedCallBacks这是一个函数数组,里边存放着尚未执行的来自then()的函数,之所以用数组是为了保证链式调用
  • onRejectCallback存放着一个handleError函数,当Promise抛出异常时,执行该函数
  • __PromiseResult__存放着resolve或者reject的结果

完善resolve、reject、then和catch

  • resolve和reject
    const resolve = (val) => {
      this.__PromiseResult__ = val;
      this.__PromiseState__ = FINISHED;
    };
    this.reject = (val) => {
      this.__PromiseResult__ = val;
      this.__PromiseState__ = FAILED;
      delete this.reject
    };

这里在跑一次reject之后就把reject从对象中删除了,是为了遵守Promise规范中reject后不可以修改拒绝的原因

  • then和catch
  then(fun,handleError) {
    try {
      if (this.__PromiseState__ === PENDING){
        this.onResolvedCallbacks.push(fun);
        this.onRejectCallback = handleError||this.onRejectCallback;
      }
      else if (this.__PromiseState__ === FINISHED) {
        let res = fun?.(this.__PromiseResult__);
        if (res) this.__PromiseResult__ = res;
      }else if (this.__PromiseState__ === FAILED) handleError?.(this.__PromiseResult__)
    } catch (error) {
      this.reject?.(error);
    }
    return this;
  }
  catch(fun) {
    return this.then(undefined,fun)
  }

then的第一个参数为状态fulfilled应该执行的函数fun,后者则是Promise then第二个参数

在运行时,如果当前state为pending则将fun塞入onResolvedCallbacks,当前state为fulfilled则直接执行fun,并且如果fun有返回值,则将返回值作为新的__PromiseResult__

用setter实现我们的异步

Object.defineProperties(this, {
      __PromiseState__: {
        set(e) {
          __PromiseState__ = e;
          if (e === FINISHED) {
            try {
              this.onResolvedCallbacks.forEach(item =>item?.(this.__PromiseResult__))
            } catch (error) {
              this.reject(error);
            }
          } else if (e === FAILED) {
            if(this.onRejectCallback instanceof Function) this.onRejectCallback?.(this.__PromiseResult__)
            else throw new Error(this.__PromiseResult__)
          }
        },
        get:()=>__PromiseState__
      },
    });

这里解释下,前文之所以额外定义一个__PromiseState__而不是直接使用this.__PromiseState__是因为getter内部调用该属性同样触发getter

当我们resolve或者reject,会触发setter,也就是这里的set

image.png

而我们此时就可以根据状态执行我们的函数了

65行完整代码

const PENDING = "pending";
const FINISHED = "fulfilled";
const FAILED = "rejected";
class XPromise {
  constructor(handleFinish) {
    let __PromiseState__ = PENDING;
    this.__PromiseState__ = __PromiseState__;
    this.onResolvedCallbacks = [];
    this.__PromiseResult__ = undefined;
    const resolve = (val) => {
      this.__PromiseResult__ = val;
      this.__PromiseState__ = FINISHED;
    };
    this.reject = (val) => {
      this.__PromiseResult__ = val;
      this.__PromiseState__ = FAILED;
      delete this.reject;
    };
    Object.defineProperties(this, {
      __PromiseState__: {
        set(e) {
          __PromiseState__ = e;
          if (e === FINISHED) {
            try {
              this.onResolvedCallbacks.forEach((item) => {
                let res = item?.(this.__PromiseResult__);
                if (res) this.__PromiseResult__ = res;
              });
            } catch (error) {
              this.reject(error);
            }
          } else if (e === FAILED) {
            if (this.onRejectCallback instanceof Function)
              this.onRejectCallback?.(this.__PromiseResult__);
            else throw new Error(this.__PromiseResult__);
          }
        },
        get: () => __PromiseState__,
      },
    });
    try {
      handleFinish?.(resolve, this.reject);
    } catch (error) {
      this.reject?.(error);
    }
  }
  then(fun, handleError) {
    try {
      if (this.__PromiseState__ === PENDING) {
        this.onResolvedCallbacks.push(fun);
        this.onRejectCallback = handleError || this.onRejectCallback;
      } else if (this.__PromiseState__ === FINISHED) {
        let res = fun?.(this.__PromiseResult__);
        if (res) this.__PromiseResult__ = res;
      } else if (this.__PromiseState__ === FAILED)
        handleError?.(this.__PromiseResult__);
    } catch (error) {
      this.reject?.(error);
    }
    return this;
  }
  catch(fun) {
    return this.then(undefined, fun);
  }
}

测试

那么我们跑一下,看看有没有效果

测试1:异步与链式调用

function test() {
  let p = new XPromise((rs, rj) => {
    setTimeout(() => {
      rs(2021);
    }, 1000);
  })
    .then((res) => {
      console.log(res);
      return 2022;
    })
    .then((res) => {
      console.log(res);
    });
}
test();

预估输出2021、2022

image.png

测试2:异常抛出

function test() {
  let p = new XPromise((rs, rj) => {
    setTimeout(() => {
      rs(2021);
    }, 1000);
  }).then(res=>{
      console.log(res)
      const i=1
      i=2
  })
}
test();

image.png
如预料一般,会在1s后输出2021;然后由于我们在then中修改const值,会抛出异常

测试3:catch捕获异常

function test() {
  let p = new XPromise((rs, rj) => {
    setTimeout(() => {
      rs(2021);
    }, 1000);
  })
    .then((res) => {
      const i=1
      i=0
      return 2022;
    })
    .catch(e=>{
      console.log(e)
    })
    
}
test();

既然捕获了,那就不会红色报错啦

image.png

那么至此,手撕Promise完成

更多

这里只实现了Promise最基本的功能,至于all、race类似的方法,就不多写啦

  • then里的回调无法放置异步函数所以我没有真的实现Promise...还是懂得不够,不行不行等我更新