手写Promise

写了一年的Promise,今天终于是把测试跑通了,其它都不是难点,难点在于resolvePromise,这是所有里面最难的,当然我们抛开这个先不谈,先实现一些基本的特性.

1. Promise 的三个基本属性

Promise有三个基本属性

  1. value:Promise成功的值
  2. reason:Promise失败的理由
  3. state:状态,有pendingfulfilledrejected状态

所以我们可以添加三个属性值,以及关于State的三个状态的常量:

const State = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected',
};

class MyPromise {
      value; // 成功的值
      reason; // 失败的理由
      state = State.pending;
 }

2. Promise 的构造器

Promise的构造器传入参数是一个函数,并传入resolvereject函数,用来改变Promise对象的状态,所以我们可以得到如下代码:

const State = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected',
};

class MyPromise {
      value;
      reason;
      state = State.pending;
      
      constructor(exector){
         exector?.(this.resolve.bind(this), this.reject.bind(this)); // 新增的代码
      } 
      
      resolve(){} // 新增的代码
      reject(){} // 新增的代码
 }

2. resolve和reject

Promise中调用这个函数,分别会将Promise的状态改为fulfilledrejected,所以加入状态的改变:

const State = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected',
};

class MyPromise {
      value;
      reason;
      state = State.pending;
      
      constructor(exector){
         exector?.(this.resolve.bind(this), this.reject.bind(this));
      } 
      
      resolve(value){
          this.value = value; // 新增的代码
          this.state = State.fulfilled; // 新增的代码
      }
      
      reject(reason){
         this.reason = reason; // 新增的代码
         this.state = State.rejected; // 新增的代码
      } 
 }

当然,Promise状态修改后,就不能再次修改,所以我们加入判断

const State = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected',
};

class MyPromise {
      value;
      reason;
      state = State.pending;
      
      constructor(exector){
         exector?.(this.resolve.bind(this), this.reject.bind(this));
      } 
      
      resolve(value){
          if (this.state !== State.pending) return; // 新增的代码

          this.value = value; 
          this.state = State.fulfilled; 
      } 
      
      reject(reason){
         if (this.state !== State.pending) return; // 新增的代码
        
         this.reason = reason; 
         this.state = State.rejected;
      } 
 }

3. then函数

then函数返回一个新的Promise对象,参数是两个函数,第一个函数是前一个promise对象被Resolve时调用,第二个函数是前一个对象被Reject时调用,所以我们加上一个then方法,并返回一个新的Promise,并且调用它的ResolveReject

const State = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected',
};

class MyPromise {
      value;
      reason;
      state = State.pending;
      
      constructor(exector){
         exector?.(this.resolve.bind(this), this.reject.bind(this));
      } 
      
      resolve(value){
          if (this.state !== State.pending) return;

          this.value = value; 
          this.state = State.fulfilled; 
      } 
      
      reject(reason){
         if (this.state !== State.pending) return;
        
         this.reason = reason; 
         this.state = State.rejected;
      } 
      
      // 新增代码
      then(onFulfilled, onRejected) {
        let onFulfilledFn;
        let onRejectedFn;
        
        const nextPromise = new MyPromise((resolve, reject) => {
          onFulfilledFn = function () {
              const result = onFulfilled(this.value)
              resolve(result); 
          };

          onRejectedFn = function () {
              const result = onRejected(this.reason);
              resolve(result);
          };
        });

        onFulfilledFn = onFulfilledFn.bind(this);
        onRejectedFn = onRejectedFn.bind(this);

        if (this.state === State.fulfilled) {
          onFulfilledFn();
        } else if (this.state === State.rejected) {
          onRejectedFn();
        }
        
        return nextPromise;
     }
 }

promise对象resolve或者reject的时候,then函数不是立即执行的,也就是说是异步的,所以,我们需要在回调外面套上queueMircrotask

    ... 省略
    
      then(onFulfilled, onRejected) {
        let onFulfilledFn;
        let onRejectedFn;
        
        const nextPromise = new MyPromise((resolve, reject) => {
          onFulfilledFn = function () {
             queueMicrotask(()=>{
              const result = onFulfilled(this.value)
              resolve(result); 
             });
          };

          onRejectedFn = function () {
              queueMircotask(()=>{
               const result = onRejected(this.reason);
               resolve(result);
              })
          };
        });

        onFulfilledFn = onFulfilledFn.bind(this);
        onRejectedFn = onRejectedFn.bind(this);

        if (this.state === State.fulfilled) {
          onFulfilledFn();
        } else if (this.state === State.rejected) {
          onRejectedFn();
        }
        
        return nextPromise;
     }
  
    ... 省略

但是此时我们执行这个例子

new MyPromise((resolve)=>{ setTimeout(()=>{ resolve(1) }) })
.then((res)=>{console.log(res)})

你会发现没有任何输出

手写Promise

这是因为刚才的then只能同步执行
手写Promise

所以我们需要用数组来保存订阅,并在状态发生变更的时候执行数组中的函数:

...省略
class MyPromise {
  ...省略
  onFulfilledFnArray = [];
  onRejectedFnArray = []; // 为了可以 let a = Promise; a.then(); a.then()}
  ...省略
}
...省略

then变更为

...省略
 then(onFulfilled, onRejected) {
        let onFulfilledFn;
        let onRejectedFn;
        
        const nextPromise = new MyPromise((resolve, reject) => {
          onFulfilledFn = function () {
             queueMicrotask(()=>{
              const result = onFulfilled(this.value)
              resolve(result); 
             });
          };

          onRejectedFn = function () {
              queueMircotask(()=>{
               const result = onRejected(this.reason);
               resolve(result);
              })
          };
        });

        onFulfilledFn = onFulfilledFn.bind(this);
        onRejectedFn = onRejectedFn.bind(this);

        if (this.state === State.fulfilled) {
          onFulfilledFn();
        } else if (this.state === State.rejected) {
          onRejectedFn();
        } else {
          // 新增代码
          this.onFulfilledFnArray.push(onFulfilledFn);
          this.onRejectedFnArray.push(onRejectedFn);
        }

        return nextPromise;
     }
...省略

修改一下resolvereject

...省略
class MyPromise {
  ...省略
   resolve(value) {
        if (this.state !== State.pending) return;

        this.value = value;
        this.state = State.fulfilled;

        // 新增代码
        while (this.onFulfilledFnArray.length > 0) {
          this.onFulfilledFnArray.shift()(); // 不是立即执行
        }
  }

  reject(reason) {
        if (this.state !== State.pending) return;

        this.reason = reason;
        this.state = State.rejected;

        // 新增代码
        while (this.onRejectedFnArray.length > 0) {
          this.onRejectedFnArray.shift()(); // 不是立即执行
        }
  }

  ...省略
}
...省略

给出当前的完整代码:

const State = {
  pending: 'pending',
  fulfilled: 'fulfilled',
  rejected: 'rejected',
};

class MyPromise {
      value;
      reason;
      onFulfilledFnArray = [];
      onRejectedFnArray = [];
      state = State.pending;
      
      constructor(exector){
         exector?.(this.resolve.bind(this), this.reject.bind(this));
      } 
      
      resolve(value) {
        if (this.state !== State.pending) return;

        this.value = value;
        this.state = State.fulfilled;

        // 新增代码
        while (this.onFulfilledFnArray.length > 0) {
          this.onFulfilledFnArray.shift()(); // 不是立即执行
        }
      }

      reject(reason) {
        if (this.state !== State.pending) return;

        this.reason = reason;
        this.state = State.rejected;

        // 新增代码
        while (this.onRejectedFnArray.length > 0) {
          this.onRejectedFnArray.shift()(); // 不是立即执行
        }
      }
      
      then(onFulfilled, onRejected) {
        let onFulfilledFn;
        let onRejectedFn;
        
        const nextPromise = new MyPromise((resolve, reject) => {
          onFulfilledFn = function () {
             queueMicrotask(()=>{
              const result = onFulfilled(this.value)
              resolve(result); 
             });
          };

          onRejectedFn = function () {
              queueMircotask(()=>{
               const result = onRejected(this.reason);
               resolve(result);
              })
          };
        });

        onFulfilledFn = onFulfilledFn.bind(this);
        onRejectedFn = onRejectedFn.bind(this);

        if (this.state === State.fulfilled) {
          onFulfilledFn();
        } else if (this.state === State.rejected) {
          onRejectedFn();
        } else {
          // 新增代码
          this.onFulfilledFnArray.push(onFulfilledFn);
          this.onRejectedFnArray.push(onRejectedFn);
        }

        return nextPromise;
     }
 }

可以发现成功输出1

手写Promise

4. resolve传递值的规则

  1. resolve不能传递同一个Promise对象,否则会报错
    这种情况一般发生在异步代码中:
const promise = new Promise((resolve, reject)=>{
   setTimeout(()=>{
       resolve(promise)
   })
})

Promise – JavaScript | MDN (mozilla.org)

手写Promise

所以我们需要写一个resolvePromise的函数,来过滤掉特殊情况

resolvePromise(value) {
   if (value === this) {
      this.reject(
        new TypeError(
          'Circular reference detected: promise and x are the same object',
        ),
      );
      return false;
    }  
}
  1. thenable对象
    当遇到thenable对象时,会调用thenable对象的then,并将resolve的值作为promise的最终状态。
  resolvePromise(value) {
    let called = false;

    if (value === this) {
       // 省略
    } else if (value instanceof MyPromise) {
      try {
        value.then(
          x => {
            if (called) return;
            called = true;
            this.resolve(x);
          },
          y => {
            if (called) return;
            called = true;
            this.reject(y);
          },
        );
        return false;
      } catch (e) {
        if (called) return;
        this.reject(e);
        return false;
      }
    } else if (value && typeof value === 'object') {
      try {
        const thenable = Reflect.get(value, 'then');
        if (typeof thenable === 'function') {
          try {
            thenable.call(
              value,
              x => {
                if (called) return;
                called = true;
                this.resolve(x);
              },
              y => {
                if (called) return;
                called = true;
                this.reject(y);
              },
            );
            return false;
          } catch (e) {
            if (called) return;
            this.reject(e);
            return false;
          }
        }
      } catch (e) {
        if (called) return;
        this.reject(e);
        return false;
      }
    } else if (value && typeof value === 'function') {
      try {
        if (Reflect.has(value, 'then')) {
          return this.resolvePromise(Reflect.get(value, 'then'));
        }
        if (value) {
          try {
            value.call(
              value,
              x => {
                if (called) return;
                called = true;
                this.resolve(x);
              },
              y => {
                if (called) return;
                called = true;
                this.reject(y);
              },
            );
            return false;
          } catch (e) {
            if (called) return;
            this.reject(e);
            return false;
          }
        }
      } catch (e) {
        if (called) return;
        this.reject(e);
        return false;
      }
    }

    return true;
  }

最后修改一下resolve函数:

  resolve(value) {
    if (this.state !== State.pending) return;
    if (!this.resolvePromise(value)) return;  // 特殊对象,特殊解析

    this.value = value;
    this.state = State.fulfilled;

    while (this.onFulfilledFnArray.length > 0) {
      this.onFulfilledFnArray.shift()(); // 不是立即执行
    }
  }

还没想好怎么写,正在施工

🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧

先给出完整代码:

const State = {
pending: 'pending',
fulfilled: 'fulfilled',
rejected: 'rejected',
};
class MyPromise {
value; // 成功的值
reason; // 失败的理由
state = State.pending;
onFulfilledFnArray = [];
onRejectedFnArray = []; // 为了可以 let a = Promise; a.then(); a.then()
constructor(exector) {
exector?.(this.resolve.bind(this), this.reject.bind(this));
}
static resolve(value) {
return new MyPromise(resolve => {
resolve(value);
});
}
static reject(value) {
return new MyPromise((resolve, reject) => {
reject(value);
});
}
resolvePromise(value) {
let called = false;
if (value === this) {
this.reject(
new TypeError(
'Circular reference detected: promise and x are the same object',
),
);
return false;
} else if (value instanceof MyPromise) {
try {
value.then(
x => {
if (called) return;
called = true;
this.resolve(x);
},
y => {
if (called) return;
called = true;
this.reject(y);
},
);
return false;
} catch (e) {
if (called) return;
this.reject(e);
return false;
}
} else if (value && typeof value === 'object') {
try {
const thenable = Reflect.get(value, 'then');
if (typeof thenable === 'function') {
try {
thenable.call(
value,
x => {
if (called) return;
called = true;
this.resolve(x);
},
y => {
if (called) return;
called = true;
this.reject(y);
},
);
return false;
} catch (e) {
if (called) return;
this.reject(e);
return false;
}
}
} catch (e) {
if (called) return;
this.reject(e);
return false;
}
} else if (value && typeof value === 'function') {
try {
if (Reflect.has(value, 'then')) {
return this.resolvePromise(Reflect.get(value, 'then'));
}
if (value) {
try {
value.call(
value,
x => {
if (called) return;
called = true;
this.resolve(x);
},
y => {
if (called) return;
called = true;
this.reject(y);
},
);
return false;
} catch (e) {
if (called) return;
this.reject(e);
return false;
}
}
} catch (e) {
if (called) return;
this.reject(e);
return false;
}
}
return true;
}
resolve(value) {
if (this.state !== State.pending) return;
if (!this.resolvePromise(value)) return;
this.value = value;
this.state = State.fulfilled;
while (this.onFulfilledFnArray.length > 0) {
this.onFulfilledFnArray.shift()(); // 不是立即执行
}
}
reject(reason) {
if (this.state !== State.pending) return;
this.reason = reason;
this.state = State.rejected;
while (this.onRejectedFnArray.length > 0) {
this.onRejectedFnArray.shift()(); // 不是立即执行
}
}
catch(onFulfilled) {
return this.then(undefined, onFulfilled);
}
then(onFulfilled, onRejected) {
let onFulfilledFn;
let onRejectedFn;
const nextPromise = new MyPromise((resolve, reject) => {
onFulfilledFn = function () {
queueMicrotask(() => {
try {
if (typeof onFulfilled === 'function') {
const result = onFulfilled(this.value);
resolve(result);
} else {
resolve(this.value);
}
} catch (e) {
reject(e);
}
});
};
onRejectedFn = function () {
queueMicrotask(() => {
try {
if (onRejected) {
if (typeof onRejected === 'function') {
const result = onRejected(this.reason);
resolve(result);
} else {
reject(this.reason);
}
} else {
reject(this.reason);
}
} catch (e) {
reject(e);
}
});
};
});
onFulfilledFn = onFulfilledFn.bind(this);
onRejectedFn = onRejectedFn.bind(this);
if (this.state === State.fulfilled) {
onFulfilledFn();
} else if (this.state === State.rejected) {
onRejectedFn();
} else {
this.onFulfilledFnArray.push(onFulfilledFn);
this.onRejectedFnArray.push(onRejectedFn);
}
return nextPromise;
}
}

代码仓库以及测试:
javascript/面试题/手写Promise/index.js at master · RadiumAg/javascript (github.com)

原文链接:https://juejin.cn/post/7357735854716895244 作者:RadiumAg

(0)
上一篇 2024年4月15日 下午4:36
下一篇 2024年4月15日 下午4:46

相关推荐

发表回复

登录后才能评论