calss实现promise

先看下Promise的基本使用

let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功");
  }, 0);
});

p.then(
  (data) => {
    console.log(data);
  },
  (error) => {
    console.log(error);
  }
);

根据我们平时使用的经验和Promise/A+规范,可以知道

  • 一个Promise有三个状态
  • 有两个更改状态的函数resolve和reject
  • 成功或失败的值
  • 成功或失败的回调函数(这里就可以使用发布订阅的模式)
  • then可以链式调用
  • Promise的参数就是一个函数并且同步执行
  • 规范里还提到then的返回值不同会有不同的处理方式

首先声明一个类,把一开始的过程用代码体现以下

const PENDING = "PENGDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class MyPromise {
  constructor(exec) {
    //状态
    this.status = PENDING;
    //resolve()传得值
    this.value = undefined;
    //reject()传得值
    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 {
      exec(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
 }

一开始仅仅是执行了exec用户传入的函数初始化状态和声明两个resolve函数和reject函数并改变Promise实例的状态,同时保证只有在状态是PENGDING时才可以更改状态

then方法

  • 返回一个新的Promise实例
  • 在不同的Promise实例状态下执行失败回调还是成功回调,或者在PENGING状态下把回调函数存储起来
//then方法返回一个新的promise
  then(onFulfilled, onRejected) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            //需要判断onFulfilled()的返回值是不是一个Promise还是普通值
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          //切片编程 AOP
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
  • 上面的代码处理了用户的回调函数,没有转换Promise2的状态,所以要根据规范写个resolvePromise来处理promise2的状态
  • 这里把onFulfilled 和onReject放进了存放回调函数的数组,我们要在一开始声明的 resolve函数里执行this.onResolvedCallbacks.forEach((fn) => fn());在reject里执行this.onRejectedCallbacks.forEach((fn) => fn());

resolvePromise

  • 利用onFulfilled或者onReject的返回值x来判断是调用promise2的resolve还是reject
  • 判断是不是thenable对象,如果是就递归调用直到返回值是普通值
  • then需要绑定this值并且传递两个回调函数
function resolvePromise(promise2, x, resolve, reject) {
  //如果x和promise2是同一个对象,就抛出类型错误
  if (x === promise2) {
    reject(new TypeError("类型错误"));
  }
  //判断是不是thenable对象 (有then函数属性并且是对象或者是函数)
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    //防止反复调用成功或者失败,只能调用一次
    let called = false;
    try {
      let then = x.then;
      if (typeof then === "function") {
        //绑定this值,并传入两个回调函数作为参数
        then.call(
          x,
          (y) => {
            //递归调用resolvePromise直到返回值不是promise
            if (called) {
              return;
            }
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) {
              return;
            }
            called = true;
            reject(r);
          }
        );
      } else {
        //{} {then:{}}
        resolve(x);
      }
    } catch (error) {
      if (called) {
        return;
      }
      called = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}

最后

感觉最重要的是
– 发布订阅处理用户传入的回调函数
– then方法如何支持链式调用

完整代码贴一下,还有一些其他的方法

const PENDING = "PENGDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

function resolvePromise(promise2, x, resolve, reject) {
  //如果x和promise2是同一个对象,就抛出类型错误
  if (x === promise2) {
    reject(new TypeError("类型错误"));
  }
  //判断是不是thenable对象 (有then函数属性并且是对象或者是函数)
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    //防止反复调用成功或者失败,只能调用一次
    let called = false;
    try {
      let then = x.then;
      if (typeof then === "function") {
        //绑定this值,并传入两个回调函数作为参数
        then.call(
          x,
          (y) => {
            //递归调用resolvePromise直到返回值不是promise
            if (called) {
              return;
            }
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) {
              return;
            }
            called = true;
            reject(r);
          }
        );
      } else {
        //{} {then:{}}
        resolve(x);
      }
    } catch (error) {
      if (called) {
        return;
      }
      called = true;
      reject(error);
    }
  } else {
    resolve(x);
  }
}
class MyPromise {
  constructor(exec) {
    //状态
    this.status = PENDING;
    //resolve()传得值
    this.value = undefined;
    //reject()传得值
    this.reason = undefined;
    //利用发布订阅存放成功或失败的回调函数,在then方法里面
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.status === PENDING) {
        //resolve()参数传入的新的promise
        if (value instanceof MyPromise) {
          return value.then(resolve, reject);
        }
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };
    try {
      exec(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  //then方法返回一个新的promise
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (error) => {
            throw error;
          };
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            //需要判断onFulfilled()的返回值是不是一个Promise还是普通值
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          //切片编程 AOP
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      resolve(value);
    });
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }
  catch(errorFn) {
    return this.then(null, errorFn);
  }
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      let result = [];
      let times = 0;
      const processSuccess = (index, value) => {
        result[index] = value;
        //计数器数和promises长度一致就调用resolve()
        if (++times === promises.length) {
          resolve(result);
        }
      };
      for (let index = 0; index < promises.length; index++) {
        const p = promises[index];
        if (p && typeof p.then === "function") {
          p.then((data) => {
            processSuccess(index, data);
          }, reject);
        } else {
          //普通值
          processSuccess(index, data);
        }
      }
    });
  }
  //谁快用谁
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      for (let index = 0; index < promises.length; index++) {
        const p = promises[index];
        if (p && typeof p.then === "function") {
          p.then(resolve, reject); //一旦成功或失败就调用resolve或者reject,停止promise
        } else {
          resolve(p);
        }
      }
    });
  }
  //图片加载失败,脚本加载超时
  static wrap(p) {
    let abort;
    let p1 = new MyPromise((resolve, reject) => {
      abort = reject;
    });
    let p2 = MyPromise.race([p, p1]);
    p2.abort = abort;
    return p2;
  }
  finally(cb) {
    return this.then(
      (data) => {
        return MyPromise.resolve(cb()).then(() => data);
      },
      (error) => {
        return MyPromise.resolve(cb()).then(() => {
          throw error;
        });
      }
    );
  }
}
发布日期:
分类:js 标签:

发表评论

您的电子邮箱地址不会被公开。