手写 Promise

1,011 阅读8分钟

手写 Promise

花了一天的时间写了这篇文章,一步一步实现一个 Promise,如果哪里有问题请大家多多指教

一、初始化 Promise 类

Promise 接收一个函数,函数内有两个参数,第一个参数是成功的回调,第二个参数是错误的回调

 class ToyPromise {
	constructor(handler) {
    		// 注意:this 的指向是由函数执行的位置决定的
		handler(this._resolve.bind(this), this._reject.bind(this));
	}
	_resolve() {}	
	_reject() {}
}      
let p = new ToyPromise((resolve, reject) => {
	console.log(1);
    	// 注意:this 的指向是由函数执行的位置决定的,这里的 this 指向的是 window
	resolve();
});

二、添加 then 方法

then 方法也接收两个参数,第一个参数是 resolve 执行的函数,第二个参数是 reject 或出现错误执行的函数

then 方法并不会立即执行传入的函数而是需要等待当前 ToyPromise 调用 resolve 或 reject 方法,们这里需要先把传入的参数保存到一个指定的位置,在 ToyPromise 调用 resolve 或 reject 方法以后再去执行 - 事件注册

class ToyPromise {
  constructor(handler) {
     // 数组:队列 - 先注册的,在调用 resolve 方法的使用,先执行的 FIFO
    this.resolvedHandler = [];
    this.rejectedHandler = [];

    handler(this._resolve.bind(this), this._reject.bind(this));
  }

  _resolve() {
    let handler;
    // 执行注册的函数
    while ((handler = this.resolvedHandler.shift())) {
      handler();
    }
  }

  _reject() {}

  then(resolvedHandler, rejectedHandler) {
    // 先把传入的 resolveHandler 保存到一个指定的位置
    this.resolvedHandler.push(resolvedHandler);
    this.rejectedHandler.push(rejectedHandler);
  }
}
let p = new ToyPromise((resolve, reject) => {
    resolve();
});

p.then(() => {
  console.log("第一个 then -- 1");
});
p.then(() => {
  console.log("第一个 then -- 2");
});

这个时候操作就会发现 then 并未执行,因为 resolve, 和 then 方法都是同步的,then 内的函数并未添加到数组中,resolve 已经执行结束了,而原生 Promise 的 then 方法内的函数执行则是异步的,所以我们就要对 resolve 方法进行一些更改,让 resolve 的代码异步执行

三、then 的异步操作

  _resolve() {
    setTimeout(() => {
      let handler;
      while ((handler = this.resolvedHandler.shift())) {
        handler();
      }
    }, 0);
  }

我们对 resolve 内的代码包裹在定时器内从而让里面的代码异步执行,但是 setTimeout 属于宏任务,而原生的 Promise 的 then 方法则是微任务,微任务在同步渲染后, DOM 渲染前执行,宏任务在 DOM 渲染后执行,所以我们就要在对 resolve 方法进行一些更改,让 resolve 的代码变成微任务

// 宏任务
setTimeout(() => {
  console.log("宏任务");
});

// postMessage
// MutationObserver
// 微任务
let ob = new MutationObserver((list) => {
  console.log("微任务");
});
//   观察者:观察 DOM 元素的变化
ob.observe(document.body, {
  attributes: true,
});
document.body.setAttribute("random", Math.random());

通过上述代码,我们可以看到 MutationObserver 比 setTimeout 先执行,所以我们可以通过 MutationObserver 来更改 resolve 方法

  // 创建 observer 方法, 接收一个回调函数,在 DOM 变化时执行
  observer(callback) {
    let ob = new MutationObserver((list) => {
      callback();
    });
    //   观察者:观察 DOM 元素的变化
    ob.observe(document.body, {
      attributes: true,
    });
    document.body.setAttribute("random", Math.random());
  }
  
  _resolve() {
    this.observer(() => {
      let handler;
      while ((handler = this.resolvedHandler.shift())) {
        handler();
      }
    });
  }

四、then 方法接收参数

  _resolve(val) {
    this.observer(() => {
      let handler;
      while ((handler = this.resolvedHandler.shift())) {
        // 将 resolve 接收的参数,传入 then 的回调函数中
        handler(val);
      }
    });
  }

五、then 方法的链式调用

1. 返回正常值

then 方法链式调用,我们现在 then 方法现在没有返回值,默认的返回值 undefined,而我们想要链式调用 then 方法,所以我们就应当让 then 方法返回一个 Promise 对象来去调用 then 方法,而不是 undefiend;

  then(resolvedHandler, rejectedHandler) {
    return new ToyPromise((resolve, reject) => {
      this.resolvedHandler.push((val) => {
        resolvedHandler(val);
        // 调用下一个 then
        resolve();
      });
    });
  }
let p = new ToyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 100);
});

p.then((val) => {
  console.log("第一个 then -- 1", val);
}).then(() => {
  console.log("链式调用第二个 then");
});

上一个 then 函数内的回调函数的返回值,是下一个 then 函数的回调函数的参数

then(resolvedHandler, rejectedHandler) {
    // this.rejectedHandler.push(rejectedHandler);
    return new ToyPromise((resolve, reject) => {
      this.resolvedHandler.push((val) => {
        let res = resolvedHandler(val);
        // 调用 then 方法,并传递参数
        resolve(res);
      });
    });
  }

2. 返回 Promise 对象

如果 then 函数内的回调函数的返回值是一个 Promise 对象,那么这个 Promise 对象不再是下个 then 方法的参数,而是调用下个 then 方法 Promise 对象。

then(resolvedHandler, rejectedHandler) {
    return new ToyPromise((resolve, reject) => {
      this.resolvedHandler.push((val) => {
        let res = resolvedHandler(val);
        // 如果 then 方法返回的是 ToyPromise 对象
        if (res instanceof ToyPromise) {
          // 给返回的是 ToyPromise 对象 添加 then 方法, 传入当前的这个 Promise 的 resolve 方法,因为下一个 then 方法注册在当前这个对象上,而不是在返回的 Promise 上
          res.then(resolve);
          return;
        }
        resolve(res);
      });
    });
  }

res 是 then 方法的回调函数 的返回值,如果 res 是 ToyPromise 对象,我们就要 res 添加 then 方法, 并传入当前的 这个 ToyPromise 对象的 resolve 方法,因为链式调用的第二个 then 方法注册在当前的这个 ToyPromise 对象上,而不是在 res 上,所以我们要给 res 添加 then 方法来执行链式调用的第二个 then 方法

let p = new ToyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 100);
});

p.then((val) => {
  console.log("第一个 then -- 1", val);
  return new ToyPromise((resolve, reject) => {
    resolve("123");
  });
}).then(() => {
  console.log("链式调用第二个 then");
});

3. 返回一个包含 then 属性的对象

我们只需要判断 res 是否是含有 then 属性的对象,如果是直接执行就可以

then(resolvedHandler, rejectedHandler) {
    return new ToyPromise((resolve, reject) => {
      this.resolvedHandler.push((val) => {
        let res = resolvedHandler(val);
        if (res instanceof ToyPromise) {
          // 给 res 添加 then 方法, resolve 后执行
          res.then(resolve);
          return;
        }
        // res 是否是含有 then 属性的 对象
        if (typeof res === "object" && res.then) {
          res.then();
          return;
        }

        resolve(res);
      });
    });
  }

六、添加状态

Promise 必然处于以下几种状态之一:

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled): 意味着操作成功完成。
  • 已拒绝(rejected): 意味着操作失败。

:状态只能更改一次

  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";

  constructor(handler) {
    this.resolvedHandler = [];
    this.rejectedHandler = [];
    // 初始状态    
    this.status = ToyPromise.PENDING;
    handler(this._resolve.bind(this), this._reject.bind(this));
  }

  _resolve(val) {
    // 判断状态是否为初始状态
    if (this.status !== ToyPromise.PENDING) return;
    // 更改状态
    this.status = ToyPromise.FULFILLED;
    this.observer(() => {
      let handler;
      while ((handler = this.resolvedHandler.shift())) {
        handler(val);
      }
    });
  }

  _reject(val) {
    if (this.status !== ToyPromise.PENDING) return;
    this.status = ToyPromise.REJECTED;
    this.observer(() => {
      let handler;
      while ((handler = this.rejectedHandler.shift())) {
        handler(val);
      }
    });
  }

七、catch 方法和错误处理

try catch 捕获异常

注意:then 执行过程中发生异常状态就会变成 rejected, 而 catch 函数执行过程中不发生异常状态就会变回 fulfilled

  constructor(handler) {
    this.resolvedHandler = [];
    this.rejectedHandler = [];
    // 初始化状态
    this.status = ToyPromise.PENDING;
    try {
      handler(this._resolve.bind(this), this._reject.bind(this));
    } catch (error) {
      this._reject(error);
    }
  }
  then(resolvedHandler, rejectedHandler) {
    return new ToyPromise((resolve, reject) => {
      // 成功状态的注册的函数
      this.resolvedHandler.push((val) => {
        // 判断参数是否为函数
        if (typeof resolvedHandler === "function") {
          // try catch 捕获异常
          try {
            let res = resolvedHandler(val);
            if (res instanceof ToyPromise) {
              // 给 res 添加 then 方法, resolve 后执行
              res.then(resolve, reject);
              return;
            }
            // thenable 含有 then 属性的 对象
            if (typeof res === "object" && res.then) {
              res.then();
              return;
            }
            resolve(res);
          } catch (error) {
          	// 发生异常执行错误的回调
            reject(error);
          }
        }
      });

      // 失败状态的注册的函数
      this.rejectedHandler.push((val) => {
        // 判断参数是否为函数
        let res;
        if (typeof rejectedHandler === "function") {
         // try catch 捕获异常
          try {
            res = rejectedHandler(val);
            if (res instanceof ToyPromise) {
              // 给 res 添加 then 方法, resolve 后执行
              res.then(resolve, reject);
              return;
            }
            // thenable 含有 then 属性的 对象
            if (typeof res === "object" && res.then) {
              res.then();
              return;
            }
            resolve(res);
          } catch (error) {
            // 发生异常执行错误的回调
            reject(error);
          }
        }
      });
    });
  }
 catch(rejectedHandler) {
    return this.then(undefined, rejectedHandler);
  }

八、 finally 方法

 constructor(handler) {
    // 数组:队列 - 先注册的,在调用resolve方法的时候,先执行, FIFO
    this.resolvedHandler = [];
    // 注册在调用 reject 方法的时候,执行的函数
    this.rejectedHandler = [];
    // 注册在调用 finally 方法的时候,执行的函数
    this.finallyHandler = [];
    // 初始化状态
    this.status = ToyPromise.PENDING;
    try {
      handler(this._resolve.bind(this), this._reject.bind(this));
    } catch (error) {
      this._reject(error);
    }
  }

  _resolve(val) {
    // 判断是否为初始化状态
    if (this.status !== ToyPromise.PENDING) return;
    this.status = ToyPromise.FULFILLED;
    this.observer(() => {
      let handler;
      while ((handler = this.resolvedHandler.shift())) {
        handler(val);
      }
      // 执行 finally 的函数
      this._finally();
    });
  }

  _reject(val) {
    // 判断是否为初始化状态
    if (this.status !== ToyPromise.PENDING) return;
    this.status = ToyPromise.REJECTED;
    this.observer(() => {
      let handler;
      while ((handler = this.rejectedHandler.shift())) {
        handler(val);
      }
      // 执行 finally 的函数
      this._finally();
    });
  }
  
  // 执行 finally 函数
  _finally() {
    let handler;
    // 因为每一个独立的Promise只处理一次任务,所以注册的回调取出以后就不再需要了
    while ((handler = this.finallyHandler.shift())) {
      handler();
    }
  }
  
  // 注册finally 函数
  finally(finallyHandler) {
    this.finallyHandler.push(finallyHandler);
  }

九、Promise.resolve

  static resolve(val) {
    return new ToyPromise((resolve, reject) => {
      resolve(val);
    });
  }
ToyPromise.resolve(123).then((val) => {
  console.log(val);
});

十、Promise.all

 static all(arr) {
    let len = arr.length;
    let values = [];
    let n = 0;
    return new ToyPromise((resolve, reject) => {
      for (let i = 0; i < len; i++) {
        arr[i].then((val) => {
          n++;
          values.push(val);
          if (len === n) {
            resolve(values);
          }
        });
      }
    });
  }
let p1 = new ToyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 100);
});
let p2 = new ToyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(2);
  }, 1000);
});
let p3 = new ToyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(3);
  }, 300);
});

ToyPromise.all([p1, p2, p3]).then((vals) => {
  console.log(vals);
});

十一、ToyPromise

源码链接

class ToyPromise {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";

  constructor(handler) {
    // 数组:队列 - 先注册的,在调用resolve方法的时候,先执行, FIFO
    this.resolvedHandler = [];
    // 注册在调用 reject 方法的时候,执行的函数
    this.rejectedHandler = [];
    // 注册在调用 finally 方法的时候,执行的函数
    this.finallyHandler = [];
    // 初始化状态
    this.status = ToyPromise.PENDING;
    try {
      handler(this._resolve.bind(this), this._reject.bind(this));
    } catch (error) {
      this._reject(error);
    }
  }

  _resolve(val) {
    // 判断是否为初始化状态
    if (this.status !== ToyPromise.PENDING) return;
    this.status = ToyPromise.FULFILLED;
    this.observer(() => {
      let handler;
      while ((handler = this.resolvedHandler.shift())) {
        handler(val);
      }
      this._finally();
    });
  }

  _reject(val) {
    // 判断是否为初始化状态
    if (this.status !== ToyPromise.PENDING) return;
    this.status = ToyPromise.REJECTED;
    this.observer(() => {
      let handler;
      while ((handler = this.rejectedHandler.shift())) {
        handler(val);
      }
      this._finally();
    });
  }

  _finally() {
    let handler;
    // 因为每一个独立的Promise只处理一次任务,所以注册的回调取出以后就不再需要了
    while ((handler = this.finallyHandler.shift())) {
      handler();
    }
  }

  observer(callback) {
    let ob = new MutationObserver((list) => {
      callback();
    });
    //   观察者:观察 DOM 元素的变化
    ob.observe(document.body, {
      attributes: true,
    });
    document.body.setAttribute("random", Math.random());
  }

  then(resolvedHandler, rejectedHandler) {
    return new ToyPromise((resolve, reject) => {
      // 成功状态的注册的函数
      this.resolvedHandler.push((val) => {
        // 判断参数是否为函数
        if (typeof resolvedHandler === "function") {
          try {
            let res = resolvedHandler(val);
            if (res instanceof ToyPromise) {
              // 给 res 添加 then 方法, resolve 后执行
              res.then(resolve, reject);
              return;
            }
            // thenable 含有 then 属性的 对象
            if (typeof res === "object" && res.then) {
              res.then();
              return;
            }
            resolve(res);
          } catch (error) {
            reject(error);
          }
        }
      });

      // 失败状态的注册的函数
      this.rejectedHandler.push((val) => {
        // 判断参数是否为函数
        let res;
        if (typeof rejectedHandler === "function") {
          try {
            res = rejectedHandler(val);
            if (res instanceof ToyPromise) {
              // 给 res 添加 then 方法, resolve 后执行
              res.then(resolve, reject);
              return;
            }
            // thenable 含有 then 属性的 对象
            if (typeof res === "object" && res.then) {
              res.then();
              return;
            }
            resolve(res);
          } catch (error) {
            alert(error);
            reject(error);
          }
        }
      });
    });
  }

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

  finally(finallyHandler) {
    this.finallyHandler.push(finallyHandler);
  }
  static resolve(val) {
    return new ToyPromise((resolve, reject) => {
      resolve(val);
    });
  }
  static all(arr) {
    let len = arr.length;
    let values = [];
    let n = 0;
    return new ToyPromise((resolve, reject) => {
      for (let i = 0; i < len; i++) {
        arr[i]
          .then(
            (val) => {
              n++;
              values.push(val);
              if (len === n) {
                resolve(values);
              }
            },
            (error) => {
              console.log(error);
            }
          )
          .catch((error) => {
            console.log(error);
            reject(error);
          });
      }
    });
  }
}