乐趣区

关于前端:假如面试官要你手写一个promise

promise

在开发中,常常须要用到 promise,promise 具备很多个性,这一次将对 promise 个性进行总结,并从零写一个 promise。

步骤一

  • Promise 特点

    • 1,创立时须要传递一个函数,否则会报错
    • 2,会给传入的函数设置两个回调函数
    • 3,刚创立的 Promise 对象状态是 pending
class MyPromise {constructor(handle) {
    // 3,刚创立的 Promise 对象状态是 pending
    this.status = "pending";
    // 1,创立时须要传递一个函数,否则会报错
    if (!this._isFunction(handle)) {throw new Error("请传入一个函数");
    }
    // 2,会给传入的函数设置两个回调函数
    handle(this._resolve.bind(this), this._reject.bind(this))
  }
  _resolve() {}
  _reject() {}
  _isFunction(fn) {return typeof fn === "function";}
}

步骤二

  • Promise 特点

    • 4,状态一旦产生扭转就不可再次扭转
    • 5,能够通过 then 来监听状态的扭转

      • 5.1,如果创立监听时,状态曾经扭转,立刻执行监听回调
      • 5.2,如果创立监听时,状态未扭转,会等状态扭转后执行
      • 5.3,同一 promise 对象能够增加多个 then 监听,状态扭转时依照注册程序顺次执行
// 定义常量保留对象的状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {constructor(handle) {
    // 3,刚创立的 Promise 对象状态是 pending
    this.status = PENDING;
    // 胜利回调的值
    this.value = undefined;
    // 失败回调的值
    this.reason = undefined;
    // 注册的胜利回调
    this.onResolvedCallbacks = [];
    // 注册的失败回调
    this.onRejectedCallbacks = [];
    // 1,创立时须要传递一个函数,否则会报错
    if (!this._isFunction(handle)) {throw new Error("请传入一个函数");
    }
    // 2,会给传入的函数设置两个回调函数
    handle(this._resolve.bind(this), this._reject.bind(this))
  }
  _resolve(value) {
    // 4,状态一旦产生扭转就不可再次扭转
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      // 5.3,同一 promise 对象能够增加多个 then 监听,状态扭转时依照注册程序顺次执行
      this.onResolvedCallbacks.forEach(fn => fn(this.value));
    }
  }
  _reject(reason) {
    // 4,状态一旦产生扭转就不可再次扭转
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = reason;
      // 5.3,同一 promise 对象能够增加多个 then 监听,状态扭转时依照注册程序顺次执行
      this.onRejectedCallbacks.forEach(fn => fn(this.reason));
    }
  }
  then(onResolved, onRejected) {
    // 判断有没有传入胜利的回调
    if (this._isFunction(onResolved)) {
      // 5.1,如果创立监听时,状态曾经扭转,立刻执行监听回调
      if (this.status === FULFILLED) {onResolved(this.value);
      }
    }
    // 判断有没有传入失败的回调
    if (this._isFunction(onRejected)) {
      // 5.1,如果创立监听时,状态曾经扭转,立刻执行监听回调
      if (this.status === REJECTED) {onRejected(this.reason);
      }
    }
    // 5.2,如果创立监听时,状态未扭转,会等状态扭转后执行
    if (this.status === PENDING) {if (this._isFunction(onResolved)) {this.onResolvedCallbacks.push(onResolved);
      }
      if (this._isFunction(onRejected)) {this.onRejectedCallbacks.push(onRejected);
      }
    }
  }
  _isFunction(fn) {return typeof fn === "function";}
}

详解 then 办法

  • 接管两个参数:胜利回调,失败回调
  • 如果 promise 失败了,然而没有注册失败监听,就会报错
  • then 办法每次执行结束都会返回一个新的 Promise 对象

    • 如果 then 办法只有胜利回调

      • 则它返回的 promise 的状态会继承以后 promise 的状态。
      • 如果以后 promise 的状态为胜利:新 promise 的值为以后 then 的胜利回调的返回值。
      • 如果以后 promise 的状态为失败:新的 promise 没有失败监听,则会报错
    • 如果 then 办法同时蕴含胜利回调、失败回调
    • 则它返回的 promise 的状态都为胜利,且值为胜利或者失败回调的返回值。
  • 回调函数的返回值

    • 如果 then 办法的胜利 / 失败回调返回的是 promise 对象

      • 则 then 办法返回的新的 promise 对象的状态由新 promise 的外部决定。
      • 且值为新 promise 的内 resolve/reject 函数传递的参数。
    • 如果 then 办法的胜利 / 失败回调返回的是一般数据类型

      • 则 then 办法返回的新的 promise 对象的状态都为胜利。
      • 且值为胜利 / 失败回调的返回值,即都会传递给新的 promise 对象胜利的回调。
    • 如果 then 办法的胜利 / 失败回调没有返回值

      • 同返回一般数据类型
  • 失败回调函数

    • 能够捕捉上一个 promise 对象的 then 办法中胜利回调函数执行时的异样

参考 前端进阶面试题具体解答

then(onResolved, onRejected) {return new MyPromise((nextResolve, nextReject) => {
      // 1. 判断有没有传入胜利的回调
      if (this._isFunction(onResolved)) {
        // 2. 判断以后的状态是否是胜利状态
        if (this.status === FULFILLED) {
          try {
            // 拿到上一个 promise 胜利回调执行的后果
            let result = onResolved(this.value);
            // console.log("result", result);
            // 判断执行的后果是否是一个 promise 对象
            if (result instanceof MyPromise) {result.then(nextResolve, nextReject);
            } else {
              // 将上一个 promise 胜利回调执行的后果传递给下一个 promise 胜利的回调
              nextResolve(result);
            }
          } catch (e) {nextReject(e);
          }
        }
      }
      // 1. 判断有没有传入失败的回调
      // if(this._isFunction(onRejected)){
      try {
        // 2. 判断以后的状态是否是失败状态
        if (this.status === REJECTED) {let result = onRejected(this.reason);
          if (result instanceof MyPromise) {result.then(nextResolve, nextReject);
          } else {nextResolve(result);
          }
        }
      } catch (e) {nextReject(e);
      }
      // }
      // 2. 判断以后的状态是否是默认状态
      if (this.status === PENDING) {if (this._isFunction(onResolved)) {
          // this.onResolvedCallback = onResolved;
          this.onResolvedCallbacks.push(() => {
            try {let result = onResolved(this.value);
              if (result instanceof MyPromise) {result.then(nextResolve, nextReject);
              } else {nextResolve(result);
              }
            } catch (e) {nextReject(e);
            }
          });
        }
        // if(this._isFunction(onRejected)){
        // this.onRejectedCallback = onRejected;
        this.onRejectedCallbacks.push(() => {
          try {let result = onRejected(this.reason);
            if (result instanceof MyPromise) {result.then(nextResolve, nextReject);
            } else {nextResolve(result);
              nextReject();}
          } catch (e) {nextReject(e);
          }
        });
        // }
      }
    });
}

详解 catch 办法

  • 其实是 then 办法的失败回调函数的语法糖
  • 如果须要同时应用 then 和 catch 办法,必须应用链式编程,不然会报错
  • 能够捕捉上一个 promise 对象的 then 办法中胜利回调函数执行时的异样
catch(onRejected) {return this.then(undefined, onRejected);
}

为啥应用 catch 时最好应用链式编程

  • 因为 then 办法只有胜利回调,所以 p2 的状态会继承 p1
  • 又因为 p2 的状态为失败,且没有对 p2 进行失败监听,所以报错
let p1 = new Promise(function (resolve, reject) {// resolve();
  reject();});
let p2 = p1.then(function () {console.log("胜利");
});
p1.catch(function () {console.log("失败 1");
});

Promise.all()

  • Promise.all(params) 特点

    • 参数为一个数组,且数组元素为 promise 类型数据
    • 返回值为一个 promise,

      • 如果所有 promise 都执行胜利

        • 返回值为所有 promise 都胜利时返回的后果的汇合
      • 如果有一个 promise 执行失败了,则返回失败的 promise
static all(list){return new MyPromise(function (resolve, reject) {let arr = [];
        let count = 0;
        for(let i = 0; i < list.length; i++){let p = list[i];
            p.then(function (value) {// arr.push(value); 留神不要这样写,会导致后果程序不对
                arr[i] = value
                count++;
                if(list.length === count){resolve(arr);
                }
            }).catch(function (e) {reject(e);
            });
        }
    });
}

Promise.race()

  • Promise.race(params) 特点

    • 参数为一个数组,且数组元素为 promise 类型数据
    • 返回值为一个 promise,且返回值为第一个胜利或者失败的 promise 的值
static race(list){return new MyPromise(function (resolve, reject) {for(let p of list){p.then(function (value) {resolve(value);
            }).catch(function (e) {reject(e);
            });
        }
    })
}
退出移动版