Promise系列二 - 实现Promise(then、catch、finally方法设计)

143 阅读5分钟

一、结构的设计

1.1 创建类

  • 接受回调函数(excutor)并执行
class MyPromise {
  constructor(executor) {
    executor();
  }
};

const promise = new MyPromise(() => {
  console.log('立即执行')
});

1.2 规定状态

  • excutor接受两个回调函数并执行
    constructor(executor) {
    
      const resolve = (value) => {};
    
      const reject = (reason) => {};
    
      executor(resolve, reject);
    }
    
    const promise = new MyPromise((resolve, reject) => {
      console.log('立即执行');
      resolve();
      reject();
    });
    
  • 定义状态常量,保留两个回调函数接收的参数
    const STATUS_PENDING = 'pending';
    const STATUS_FUFILLED = 'fulfilled';
    const STATUS_REJECTED = 'rejected';
    
    class MyPromise {
      constructor(executor) {
        this.state = STATUS_PENDING;
        this.value = undefined;
        this.reason = undefined;
    
        const resolve = (value) => {
          this.value = value;
        };
    
        const reject = (reason) => {
          this.reason = reason;
        };
        
        executor(resolve, reject);
      }
    };
    
    const promise = new MyPromise((resolve, reject) => {
      console.log('立即执行');
      resolve('fulfilled');
      reject('rejected');
    });
    
  • 规定状态,两个回调函数只会执行一个,完整代码
    // 2.1.定义状态常量;
    const STATUS_PENDING = 'pending';
    const STATUS_FUFILLED = 'fulfilled';
    const STATUS_REJECTED = 'rejected';
    
    class MyPromise {
      constructor(executor) {
        this.state = STATUS_PENDING;
        // 2.2.保留两个回调函数接收的参数;
        this.value = undefined;
        this.reason = undefined;
    
        // 2.传入两个回调函数并规定状态,二者只执行一个;
        const resolve = (value) => {
          if (this.state === STATUS_PENDING) {
            this.state = STATUS_FUFILLED;
            this.value = value;
            console.log('resolve', value);
          }
        };
    
        const reject = (reason) => {
          if (this.state === STATUS_PENDING) {
            this.state = STATUS_REJECTED;
            this.reason = reason;
            console.log('reject', reason);
          }
        };
    
        // 1.接收回调函数并执行;
        executor(resolve, reject);
      }
    };
    
    const promise = new MyPromise((resolve, reject) => {
      console.log('立即执行');
      resolve('fulfilled');
      reject('rejected');
    });
    

二、then方法设计

  • 定义then方法,接受两个回调函数并保留
    const STATUS_PENDING = 'pending';
    const STATUS_FUFILLED = 'fulfilled';
    const STATUS_REJECTED = 'rejected';
    
    class MyPromise {
      constructor(executor) {
        this.state = STATUS_PENDING;
        this.value = undefined;
        this.reason = undefined;
    
        const resolve = (value) => {
          if (this.state === STATUS_PENDING) {
            this.state = STATUS_FUFILLED;
            
            // 3.1.推入微任务队列调用;
            queueMicrotask(() => {
              this.value = value;
              this.onFufilled(this.value)
            });
          }
        };
    
        const reject = (reason) => {
          if (this.state === STATUS_PENDING) {
            this.state = STATUS_REJECTED;
    
            queueMicrotask(() => {
              this.reason = reason;
              this.onRejected(this.reason);
            });
          }
        };
    
        executor(resolve, reject);
      }
    
      // 3.定义then方法,接受两个回调函数并保留;
      then(onFufilled, onRejected) {
        this.onFufilled = onFufilled;
        this.onRejected = onRejected;
      }
    };
    
    const promise = new MyPromise((resolve, reject) => {
      console.log('立即执行');
      resolve(111);
      reject(222);
    });
    
    promise.then(res => {
      console.log('res: ', res);
    }, err => {
      console.log('err: ', err);
    });
    

为什么要使用queueMicrotask这个API呢?

因为当我们调用resolve/reject时,此时还没执行到promise.then的代码,所以当前类并未保留这两个函数,会报错不是一个方法,因此推入到微任务队列中执行。

三、then方法优化一

3.1. 解决定时器里调用then方法

setTimeout(() => {
  promise.then(res => {
    console.log('res: ', res);
  }, err => {
    console.log('err: ', err);
  })
}, 1000)

当我们这样调用then时,因为定时器是宏任务,所以queueMicrotask里的代码先执行,调用this.onFufilled/this.onRejected会报错。

正确方式应该如下:

const resolve = (value) => {
  if (this.state === STATUS_PENDING) {

    queueMicrotask(() => {
      if (this.state !== STATUS_PENDING) return;
      this.state = STATUS_FUFILLED;
      this.value = value;
    });
  }
};

const reject = (reason) => {
  if (this.state === STATUS_PENDING) {

    queueMicrotask(() => {
      if (this.state !== STATUS_PENDING) return;
      this.state = STATUS_REJECTED;
      this.reason = reason;
    });
  }
};

then(onFufilled, onRejected) {
  // this.onFufilled = onFufilled;
  // this.onRejected = onRejected;
  if (this.state === STATUS_FUFILLED && onFufilled) {
    onFufilled(this.value)
  };
  if (this.state === STATUS_REJECTED && onRejected) {
    onRejected(this.reason)
  };
}

3.2. 解决多次调用后者覆盖前者,完整代码

const STATUS_PENDING = 'pending';
const STATUS_FUFILLED = 'fulfilled';
const STATUS_REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    this.state = STATUS_PENDING;
    this.value = undefined;
    this.reason = undefined;

    this.onFufilledFns = [];
    this.onRejectedFns = [];

    const resolve = (value) => {
      if (this.state === STATUS_PENDING) {

        queueMicrotask(() => {
          if (this.state !== STATUS_PENDING) return;
          this.state = STATUS_FUFILLED;
          this.value = value;
          this.onFufilledFns.forEach(fn => {
            fn(this.value)
          });
        });
      }
    };

    const reject = (reason) => {
      if (this.state === STATUS_PENDING) {

        queueMicrotask(() => {
          if (this.state !== STATUS_PENDING) return;
          this.state = STATUS_REJECTED;
          this.reason = reason;
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          });
        });
      }
    }

    executor(resolve, reject);
  }

  then(onFufilled, onRejected) {
    if (this.state === STATUS_FUFILLED && onFufilled) {
      onFufilled(this.value)
    };
    if (this.state === STATUS_REJECTED && onRejected) {
      onRejected(this.reason)
    };

    // 4.解决多次调用then方法被后者覆盖前者;
    if (this.state === STATUS_PENDING) {
      this.onFufilledFns.push(onFufilled);
      this.onRejectedFns.push(onRejected);
    }
  }
};

const promise = new MyPromise((resolve, reject) => {
  resolve(111);
  // reject(222);
});

promise.then(res => {
  console.log('res: ', res);
}, err => {
  console.log('err: ', err);
});

promise.then(res => {
  console.log('res2: ', res);
}, err => {
  console.log('err2: ', err);
});

setTimeout(() => {
  promise.then(res => {
    console.log('res3: ', res);
  }, err => {
    console.log('err3: ', err);
  });
}, 1000);

四、then方法优化二

4.1. 解决then的返回值是一个Promise

  then(onFufilled, onRejected) {
    // 5.解决then的返回值是一个Promise;
    return new MyPromise((resolve, reject) => {
      if (this.state === STATUS_FUFILLED && onFufilled) {
        resolve(onFufilled(this.value));
      };
      if (this.state === STATUS_REJECTED && onRejected) {
        resolve(onRejected(this.reason));
      };

      if (this.state === STATUS_PENDING) {
        this.onFufilledFns.push(() => {
          const value = onFufilled(this.value);
          resolve(value)
        });
        this.onRejectedFns.push(() => {
          const reason = onRejected(this.reason);
          resolve(reason)
        });
      };
    });
  }

  promise.then(res => {
    console.log('res: ', res);
  }, err => {
    console.log('err: ', err);
  }).then(res => {
    console.log('res2: ', res);
  });

4.2. 解决捕获错误,完整代码

const STATUS_PENDING = 'pending';
const STATUS_FUFILLED = 'fulfilled';
const STATUS_REJECTED = 'rejected';

// 5.3.封装工具函数
function execFnWithCatchErr(execFn, value, resolve, reject) {
  try {
    const result = execFn(value);
    resolve(result);
  } catch (err) {
    reject(err);
  }
};

class MyPromise {
  constructor(executor) {
    this.state = STATUS_PENDING;
    this.value = undefined;
    this.reason = undefined;

    this.onFufilledFns = [];
    this.onRejectedFns = [];

    const resolve = (value) => {
      if (this.state === STATUS_PENDING) {

        queueMicrotask(() => {
          if (this.state !== STATUS_PENDING) return;
          this.state = STATUS_FUFILLED;
          this.value = value;
          this.onFufilledFns.forEach(fn => {
            fn(this.value)
          });
        });
      }
    };

    const reject = (reason) => {
      if (this.state === STATUS_PENDING) {

        queueMicrotask(() => {
          if (this.state !== STATUS_PENDING) return;
          this.state = STATUS_REJECTED;
          this.reason = reason;
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          });
        });
      }
    }

    // 5.2.捕获executor中的error
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFufilled, onRejected) {
    // 5.解决then的返回值是一个Promise;
    return new MyPromise((resolve, reject) => {
      if (this.state === STATUS_FUFILLED && onFufilled) {
        // try {
        //   const value = onFufilled(this.value);
        //   resolve(value);
        // } catch (err) {
        //   reject(err);          
        // }
        execFnWithCatchErr(onFufilled, this.value, resolve, reject)
      }
      if (this.state === STATUS_REJECTED && onRejected) {
        // try {
        //   const reason = onRejected(this.reason);
        //   resolve(reason);
        // } catch (err) {
        //   reject(err);
        // }
        execFnWithCatchErr(onRejected, this.reason, resolve, reject)
      }

      if (this.state === STATUS_PENDING) {
        // 5.1.push一个函数;
        this.onFufilledFns.push(() => {
          // try {
          //   const value = onFufilled(this.value);
          //   resolve(value);
          // } catch (err) {
          //   reject(err)
          // }

          execFnWithCatchErr(onFufilled, this.value, resolve, reject)
        });
        this.onRejectedFns.push(() => {
          // try {
          //   const reason = onRejected(this.reason);
          //   resolve(reason);
          // } catch (err) {
          //   reject(err);
          // }

          execFnWithCatchErr(onRejected, this.reason, resolve, reject)
        });
      }
    })

  }
};

const promise = new MyPromise((resolve, reject) => {
  // resolve(111);
  reject(222);

  // throw new Error('executor error')
});

promise.then(res => {
  console.log('res: ', res);
  return 'aaa';
  // throw new Error('a error')
}, err => {
  console.log('err: ', err);
  return 'bbb';
  // throw new Error('b error')
}).then(res => {
  console.log('res2: ', res);
}, err => {
  console.log('err2: ', err);
});

五、Promise.prototype.catch()

1)catch() 方法返回一个Promise,并且处理拒绝的情况;

2)它的行为与调用Promise.prototype.then(undefined, onRejected)相同;

3)事实上obj.catch(onRejected)内部调用obj.then(undefined, onRejected)

  • 定义catch方法,实际上就是调用then方法
    // 6.定义catch方法;
    catch(onRejected) {
      this.then(undefined, onRejected);
    }
    
  • 没有调用catch方法时,就抛出到上一个promise里面的第二个函数
    then(onFufilled, onRejected) {
      // 6.1.
      const defaultOnRejected = err => { throw err };
      onRejected = onRejected || defaultOnRejected;
    
      return new MyPromise((resolve, reject) => {
        if (this.state === STATUS_FUFILLED && onFufilled) {
          execFnWithCatchErr(onFufilled, this.value, resolve, reject)
        };
        if (this.state === STATUS_REJECTED && onRejected) {
          execFnWithCatchErr(onRejected, this.reason, resolve, reject)
        };
    
        if (this.state === STATUS_PENDING) {
          if (onFufilled) this.onFufilledFns.push(() => {
            execFnWithCatchErr(onFufilled, this.value, resolve, reject)
          });
          if (onRejected) this.onRejectedFns.push(() => {
            execFnWithCatchErr(onRejected, this.reason, resolve, reject)
          });
        };
      });
    }
    
  • 结果
    const promise = new MyPromise((resolve, reject) => {
      // resolve(111);
      reject(222);
    });
    
    promise.then(res => {
      console.log('res: ', res);
    }
    // , err => {
    //   console.log('err', err);
    // }
    ).catch(err => {
      console.log('catch err', err);
    });
    

六、Promise.prototype.finally()

finally()  方法返回一个Promise。在 promise 结束时,无论结果是 fulfilled 或者是 rejected,都会执行指定的回调函数。

  • 定义finally方法
    // 7.定义finally方法;
    finally(onFinally) {
      this.then(() => {
        onFinally()
      }, () => {
        onFinally()
      })
    }
    
  • catch需要有返回值才能链式调用finally
    catch(onRejected) {
      // 7.1.返回值
      return this.then(undefined, onRejected);
    }
    

七、完整代码:

const STATUS_PENDING = 'pending';
const STATUS_FUFILLED = 'fulfilled';
const STATUS_REJECTED = 'rejected';

function execFnWithCatchErr(execFn, value, resolve, reject) {
  try {
    const result = execFn(value);
    resolve(result);
  } catch (err) {
    reject(err);
  }
};

class MyPromise {
  constructor(executor) {
    this.state = STATUS_PENDING;
    this.value = undefined;
    this.reason = undefined;
    
    this.onFufilledFns = [];
    this.onRejectedFns = [];

    const resolve = (value) => {
      if (this.state === STATUS_PENDING) {
        
        queueMicrotask(() => {
          if (this.state !== STATUS_PENDING) return;
          this.state = STATUS_FUFILLED;
          this.value = value;
          this.onFufilledFns.forEach(fn => {
            fn(this.value)
          });
        });
      }
    };
    
    const reject = (reason) => {
      if (this.state === STATUS_PENDING) {
        
        queueMicrotask(() => {
          if (this.state !== STATUS_PENDING) return;
          this.state = STATUS_REJECTED;
          this.reason = reason;
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          });
        });
      }
    }

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

  then(onFufilled, onRejected) {
    const defaultOnRejected = err => { throw err };
    onRejected = onRejected || defaultOnRejected;

    const defaultOnFulfilled = value => { return value };
    onFufilled = onFufilled || defaultOnFulfilled;

    return new MyPromise((resolve, reject) => {
      if (this.state === STATUS_FUFILLED && onFufilled) {
        execFnWithCatchErr(onFufilled, this.value, resolve, reject)
      };
      if (this.state === STATUS_REJECTED && onRejected) {
        execFnWithCatchErr(onRejected, this.reason, resolve, reject)
      };

      if (this.state === STATUS_PENDING) {
        if (onFufilled) this.onFufilledFns.push(() => {
          execFnWithCatchErr(onFufilled, this.value, resolve, reject)
        });
        if (onRejected) this.onRejectedFns.push(() => {
          execFnWithCatchErr(onRejected, this.reason, resolve, reject)
        });
      }
    });
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  finally(onFinally) {
    this.then(() => {
      onFinally()
    }, () => {
      onFinally()
    })
  }
};

const promise = new MyPromise((resolve, reject) => {
  resolve(111);
  // reject(222);
});

promise.then(res => {
  console.log('res: ', res);
  return 'dsadsad'
}
// , err => {
//   console.log('err', err);
// }
)
.then(res => {
  console.log('res2: ', res);
})
.catch(err => {
  console.log('err', err);
}).finally(() => {
  console.log('finally');
})