带你手写 Promise,别再死记硬背了!!!(下)

简介: 带你手写 Promise,别再死记硬背了!!!

3. 实现 then 链式调用 & then 穿透 & 异步操作 & 错误捕获

  • PS:then 穿透指当某个 then 操作中没有对应的回调处理,把当前 promise 的值透传给一下个 then. 例如:

image.png

  • 问题暴露:
  • 未实现多个 then 间的链式调用
  • 未实现 then 穿透
  • 未实现 resolve & reject 异步
  • 未捕获所有回调函数可能出现的错误,并传递给下一个 then 的 onReject 回调
  • 解决问题:
  • then 中返回一个新的 promise
  • 对 then 中接收的参数做兼容处理
  • 调用回调时,通过 setTimeout 包裹处理
  • 通过 try catch 实现错误捕获,然后调用 promise 中的 reject
// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接收⼀个函数参数,该函数会⽴即执⾏
class MyPromise {
  constructor(fn) {
    this.value;
    this.status = PENDING;// 默认状态
    this.onResolveCallBack = [];// 缓存 onResolve 
    this.onRejectCallBack = [];// 缓存 onReject 
    // 这里使用 try catch 捕获中可能发生的错误
    try {
      // 这里必须要绑定 this,否则在外部调用时 this 就不会执行当前实例
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject.bind(this, error);
    }
  }
  resolve(value) {
    if (this.status === PENDING) {
      this.value = value;
      this.status = RESOLVED;
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
        // 遍历调用 onResolveCallBack
        this.onResolveCallBack.forEach(r => r());
      });
    }
  }
  reject(reason) {
    if (this.status === PENDING) {
      this.value = reason;
      this.status = REJECTED;
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
        // 遍历调用 onRejectCallBack
        this.onRejectCallBack.forEach(r => r());
      });
    }
  }
}
MyPromise.prototype.then = function (onResolve, onReject) {
  // 保证 onResolve & onReject 为函数
  // 主要是为了 .then().then((v)=>v) 的情况,称之为 then 穿透
  onResolve = typeof onResolve === 'function' ? onResolve : (v) => v;
  onReject = typeof onReject === 'function' ? onReject : (v) => v;
  // 这里是为了实现链式操作
  return new MyPromise((resovle, reject) => {
    // 当前 promise 实例调用了 resolve
    if (this.status === RESOLVED) {
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
        try {
          let result = onResolve(this.value);
          resovle(result); // 下一个 promise 的状态为 fulfilled 
        } catch (error) {
          reject(error);
        }
      });
    }
    // 当前 promise 实例调用了 reject
    if (this.status === REJECTED) {
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
        try {
          let result = onReject(this.value);
          resovle(result); // 下一个 promise 的状态为 fulfilled
        } catch (error) {
          reject(error);
        }
      });
    }
    // 当前 promise 状态为 pending,把当前的 onResolve & onReject 缓存起来
    if (this.status === PENDING) {
      this.onResolveCallBack.push(() => {
        try {
          let result = onResolve(this.value);
          resovle(result); // 下一个 promise 的状态为 fulfilled 
        } catch (error) {
          reject(error);
        }
      });
      this.onRejectCallBack.push(() => {
        try {
          let result = onReject(this.value);
          resovle(result); // 下一个 promise 的状态为 fulfilled
        } catch (error) {
          reject(error);
        }
      });
    }
  });
}
复制代码

5. then 中的逻辑判断

  • then 手动返回 promise 实例,下一个 then 操作要依据返回的 promise 状态
  • 不允许返回当前 promise 实例本身
  • 抽取重复逻辑
// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接收⼀个函数参数,该函数会⽴即执⾏
class MyPromise {
  constructor(fn) {
    this.value;
    this.status = PENDING;// 默认状态
    this.onResolveCallBack = [];// 缓存 onResolve 
    this.onRejectCallBack = [];// 缓存 onReject 
    // 这里使用 try catch 捕获中可能发生的错误
    try {
      // 这里必须要绑定 this,否则在外部调用时 this 就不会执行当前实例
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject.bind(this, error);
    }
  }
  resolve(value) {
    if (this.status === PENDING) {
      this.value = value;
      this.status = RESOLVED;
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
        // 遍历调用 onResolveCallBack
        this.onResolveCallBack.forEach(r => r());
      });
    }
  }
  reject(reason) {
    if (this.status === PENDING) {
      this.value = reason;
      this.status = REJECTED;
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
        // 遍历调用 onRejectCallBack
        this.onRejectCallBack.forEach(r => r());
      });
    }
  }
}
MyPromise.prototype.then = function (onResolve, onReject) {
  // 保证 onResolve & onReject 为函数
  // 主要是为了 .then().then((v)=>v) 的情况,称之为 then 穿透
  onResolve = typeof onResolve === 'function' ? onResolve : (v) => v;
  onReject = typeof onReject === 'function' ? onReject : (v) => v;
  // 这里是为了实现链式操作
  let promise = new MyPromise((resolve, reject) => {
    // 当前 promise 实例调用了 resolve
    if (this.status === RESOLVED) {
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
          let result = onResolve(this.value);
          transferPromiseResult(promise, result, resolve, reject);
      });
    }
    // 当前 promise 实例调用了 reject
    if (this.status === REJECTED) {
      // setTimeout 为了保证异步顺序执行
      setTimeout(() => {
          let result = onReject(this.value);
          transferPromiseResult(promise, result, resolve, reject);
      });
    }
    // 当前 promise 状态为 pending,把当前的 onResolve & onReject 缓存起来
    if (this.status === PENDING) {
      this.onResolveCallBack.push(() => {
          let result = onResolve(this.value);
          transferPromiseResult(promise, result, resolve, reject);
      });
      this.onRejectCallBack.push(() => {
          let result = onReject(this.value);
          transferPromiseResult(promise, result, resolve, reject);
      });
    }
  });
  return promise;
}
// 将上一次的 promise 的值,传递给下一个 then
function transferPromiseResult(promise, result, resolve, reject) {
  // 为了处理当前的 promise 实例,在当前的 then 被返回
  if (promise === result) {
    throw new TypeError('Chaining cycle detected for promise #<MyPromise>');
  }
  try {
    // 如果上一个 then 返回的是 MyPromise 的实例 && 不是同一个 promise 实例
    // 那只需要把 MyPromise 中的处理好的返回值传递给一下 then 即可
    if (result instanceof MyPromise) {
      result.then(resolve, reject);
    } else {
      // 正常结果,传给下一个 then
      resolve(result);
    }
  } catch (error) {
    reject(error);
  }
}
复制代码

6. 实现 MyPromise.all() & MyPromise.race()

  • 上面我们实现了大部分的功能,这里的 all 和 race 都只需要基于已有功能实现即可.
  • all --> 接收多个 promise 的数组,当全部 promise 执行完,且状态都为 fulfilled,则返回结果集,否则返回失败的结果.
  • race --> 接收多个 promise 的数组,结果取最先完成的 promise 的结果,无论状态是 fulfilled 或者是 rejected.
// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接收⼀个函数参数,该函数会⽴即执⾏
class MyPromise {
  constructor(fn) {
    this.value;
    this.status = PENDING;
    this.onResolveCallBack = [];
    this.onRejectCallBack = [];
    // 执行传入fn
    try {
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      // 发生错误时,统一用 reject 方法处理
      this.reject(error);
    }
  }
  // 实例方法 resolve
  resolve(value) {
    if (this.status === PENDING) {
      this.value = value;
      this.status = RESOLVED;
      // setTimeout 保证异步执行
      setTimeout(() => {
        // 执行在 then 中存储的回调
        try {
          this.onResolveCallBack.forEach((fn) => fn && fn(this.value));
        } catch (error) {
          this.onRejectCallBack.forEach((fn) => fn && fn(error));
        }
      });
    }
  }
  // 实例方法 reject
  reject(reason) {
    if (this.status === PENDING) {
      this.value = reason;
      this.status = REJECTED;
      // setTimeout 保证异步执行
      setTimeout(() => {
        // 执行在 then 中存储的回调
        this.onRejectCallBack.forEach((fn) => fn && fn(this.value));
      });
    }
  }
  // 静态方法 resolve
  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      if (value instanceof MyPromise) {
        value.then(resolve, reject);
      } else {
        resolve(value);
      }
    });
  }
  // 静态方法 reject
  static reject(value) {
    console.log(value);
    return new MyPromise((resolve, reject) => {
      if (value instanceof MyPromise) {
        value.then(resolve, reject);
      } else {
        reject(value);
      }
    });
  }
  // 静态方法 all
  static all(promiseArr) {
    // 记录每个成功的 promise 结果
    const resolveResultArr = [];
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach(promise => {
        promise.then(value => {
          resolveResultArr.push(value);
          // 当两者的长度一致,表明所有 promise 执行完成,并结果都是成功的
          if (promiseArr.length === resolveResultArr.length) {
            resolve(resolveResultArr);
          }
        }, reason => {
          // 只要一个 promise 失败,就是失败
          reject(reason);
        });
      });
    });
  }
  // 静态方法 race
  static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach(promise => {
          promise.then(value => {
            resolve(value);
          }, reason => {
            reject(reason);
          });
      });
    });
  }
}
MyPromise.prototype.then = function (onResolve, onReject) {
  // 保证 onResolve & onReject 为函数
  // 主要是为了 .then().then((v)=>v) 的情况,称之为 then 穿透
  onResolve = typeof onResolve === 'function' ? onResolve : (v) => v;
  onReject = typeof onReject === 'function' ? onReject : (v) => v;
  let promise = new MyPromise((resolve, reject) => {
    if (this.status === RESOLVED) {
      setTimeout(() => {
        // 把上一个 then 中返回的值保存 result
        let result = onResolve(this.value);
        // 传递值下一个 then
        transferPromiseResult(promise, result, resolve, reject);
      });
    }
    if (this.status === REJECTED) {
      setTimeout(() => {
        // 相当于把上一个 then 中返回的值保存 result
        let result = onReject(this.value);
        // 传递值下一个 then
        transferPromiseResult(promise, result, resolve, reject);
      });
    }
    // 在这里通过数组存储对应的回调,方便在状态改变之后进行调用
    if (this.status === PENDING) {
      this.onResolveCallBack.push(() => {
        // 把上一个 then 中返回的值保存 result
        let result = onResolve(this.value);
        // 传递值下一个 then
        transferPromiseResult(promise, result, resolve, reject);
      });
      this.onRejectCallBack.push(() => {
        // 相当于把上一个 then 中返回的值保存 result
        let result = onReject(this.value);
        // 传递值下一个 then
        transferPromiseResult(promise, result, resolve, reject);
      });
    }
  });
  return promise;
}
// 将上一次的 promise 的值,传递给下一个 then
function transferPromiseResult(promise, result, resolve, reject) {
  // 为了处理当前的 promise 实例,在当前的 then 被返回
  if (promise === result) {
    throw new TypeError('Chaining cycle detected for promise #<MyPromise>');
  }
  try {
    // 如果上一个 then 返回的是 MyPromise 的实例 && 不是同一个 promise 实例
    // 那只需要把 MyPromise 中的处理好的返回值传递给一下 then 即可
    if (result instanceof MyPromise) {
      result.then(resolve, reject);
    } else {
      // 正常结果,传给下一个 then
      resolve(result);
    }
  } catch (error) {
    reject(error);
  }
}
复制代码

完结


目录
相关文章
|
前端开发
手写基础 promise
手写基础 promise
64 0
|
存储 前端开发
|
前端开发
Promise的用法&原理&手写实现-2
Promise的用法&原理&手写实现-2
40 1
|
5月前
|
存储 前端开发 API
技术笔记:Promise的原理探究及手写Promise
技术笔记:Promise的原理探究及手写Promise
34 0
|
6月前
|
前端开发 JavaScript API
手写promise
手写promise
40 0
|
6月前
|
存储 前端开发 JavaScript
面试官:请手写一个Promise
面试官:请手写一个Promise
|
前端开发 JavaScript API
Promise的用法&原理&手写实现-1
Promise的用法&原理&手写实现-1
54 0
|
设计模式 JSON 前端开发
前端面试必看(手写Promise+js设计模式+继承+函数柯里化等)JavaScript面试全通关(1/3)
前端面试必看(手写Promise+js设计模式+继承+函数柯里化等)JavaScript面试全通关(1/3)
73 0
|
前端开发 算法 安全
前端面试100道手写题(1)—— 手写Promise实现
今年的金三银四面试,遇到了很多新的面试八股文,其实心里对手写题或者算法题有一定抵触,因为实际工作中基本上就不会用到这些东西,但是正因为这些基础八股文,才能真正验证一个人对技术有多热爱的程度。也有可能近几年没有对这些基础知识进行巩固,所以干脆一狠心,先立个flag, 准备完成100道手写题。
256 0
|
前端开发
手写Promise
手写Promise