笔试题-手写promise

121 阅读5分钟

手写Promise A+规范

用法

  • promise 存在三个状态,成功态fulfilled,失败态rejected,等待态pending
    • 执行器中调用resolve方法,则该promise的状态从pending等待态改为fulfilled成功态
    • 同理,在执行器中调用了reject方法,状态从pending等待态改为rejected失败态
    • 如果在promise中既没有调用resolve,也没有调用reject,则该promise一直为等待态pending
  • promise通过then方法获取resolvereject传入的值then((value)=>{}, (reason)=>{})
  • then的返回值()
    • 不存在返回值时,则默认表示return undefined
    • 返回值为普通值时,则表示返回了一个成功状态且值为普通值的promise
    • then中throw Error时,表示返回了一个失败态且error为Error信息的promise
  • 注意
    • promise实例的状态如果发生变化,那么该实例的状态一直是改变后的,且不可逆转
    • promise实例化时传入的执行器函数会被立即执行
    • 如果执行器中发生异常,异常信息会被作为then方法第二个参数函数的参数。
    • then方法可以存在返回值,若存在的返回值则返回值的状态会传给第二个then方法
    • then中不要返回实例promise会造成循环引用
let p = new Promise((resolve,reject) => {
  resolve(1)
}).then((res) => {
  return p;
})
p.then((res1) => {
  console.log(res1)
})
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

实现 constructor、then、catch

  • constructor 主要是调用 new Promise时传入的exector,并传入resolve方法、reject方法
  • then方法 、核心就三步判断状态
    • pending将传入的两个方法放到对应的数组中,等待你调用resolve或reject的时候执行这俩其中一个
    • 注意: 放到数组中的方法是处理之后的,用于then方法返回的是个promise、此时需要知道你传入then方法的两个函数的返回值是个啥,因此需要resolvePromise去处理
    • rejected直接调用错误方法传递你在reject中传递的error
    • fullfiled直接调用成功方法传递你在resolve中传递的value
  • catch方法,调用then方法,直接把catch传的内容转给then方法的第二个参数

then方法的核心点 resolvePromise(promise2, value, resolve, reject)

用于处理then方法中传递参数的情况

  1. 判断promise2 与 value是不是同一个,防止存在重复引用的情况
let p = new Promise((resolve,reject) => {
  resolve(1)
}).then((res) => {
  return p;
})
p.then((res1) => {
  console.log(res1)
})
// Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
  1. 判断value是不是promise或者是thenable,不是执行resolve(value) 结束
  2. 利用try...catch...包裹获取then方法的流程,防止获取时出错,如果出错执行reject(err) 结束
  3. 判断这个then方法是不是函数,如果不是resolve(value) 结束
  4. 调用then方法then.call(value, (v) => {...}, reject)
    • 如果执行成功方法,则递归调用resolvePromise目的是防止这个then方法返回值是个promise
    • 如果执行失败方法,直接reject(error)
// then方法的三个状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

// 判断是不是个promise
const isPromise = (p) =>
  typeof p === "function" ||
  Object.prototype.toString.call(p) === "[object Object]";

class Promise {
  constructor(exector) {
    this.status = PENDING; // 状态 
    this.value = ""; // 成功值
    this.reason = ""; // 失败值
    this.onFulfilledList = []; // then方法中的成功回调
    this.onRejectedList = []; // 失败回调
 
    function resolve(value) {
      // 非PENDING状态不处理
      if (this.status !== PENDING) return;
      // 如果resolve(new Promise...) 则可处理
      if (value instanceof Promise) {
        value.then(resolve, reject);
      }
      // 修改value与状态
      this.value = value;
      this.status = FULFILLED;
      // 处理调用then方法时,promise还是等待状态情况下传入的成功与失败的回调
      if (this.onFulfilledList.length > 0) {
        this.onFulfilledList.forEach((item) => item());
      }
    }
    function reject(reason) {
      if (this.status !== PENDING) return;
      this.reason = reason;
      this.status = REJECTED;
      if (this.onRejectedList.length > 0) {
        this.onRejectedList.forEach((item) => item());
      }
    }
    // 直接调用执行器,捕获错误 比如throw Error的情况
    try {
      exector(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  // then 方法
  then(onFulfilled, onRejected) {
    // 为了方便透传 处理then方法传入的这两个参数
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };
    // 为了可以继续 .then 返回一个promise
    const promise2 = new Promise((resolve, reject) => {
      if (this.status === FULFILLED) { // 此时已经成功了
        // 由于在resolvePromise中传入的promise2
        // 如果不放在异步函数中 resolvePromise的promise2 提示未被初始化就使用
        // 可以放在任何异步任务中,除queueMicrotask
        setTimeout(() => { 
          try { // 因为传入的成功方法有可能执行错误,因此try...catch一下
            const x = onFulfilled(this.value); // 获取then方法的返回值 通常为undefined
            // 有可能返回的是promise,处理一下
            resolvePromise(promise2, x, resolve, reject); 
          } catch (e) {
            // 直接是错误的
            reject(e);
          }
        });
      }
      if (this.status === REJECTED) {
        // 异常状态同fulfilled状态同理
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      }
      if (this.status === PENDING) {
        // pending状态时,不知道是调用哪个,因此先存起来,等用的时候再调用
        this.onFulfilledList.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
        this.onRejectedList.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
      }
    });
    return promise2;
  }
  // catch 方法
  catch(errFn) {
    return this.then(null, errFn);
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    throw new Error("循环引用");
  }
  if (isPromise(x)) {
    try {
      const then = x.then;
      if (typeof then === "function") {
        let valid = false; // 防止他人Promise 重复执行这个逻辑
        then.call(
          x,
          (value) => {
            if (valid === true) return;
            valid = true;
            resolvePromise(promise2, value, resolve, reject);
          },
          (err) => {
            if (valid === true) return;
            valid = true;
            reject(err);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      reject(e);
    }
  } else {
    resolve(x);
  }
}

其他方法

Resolve,Reject (静态方法)

Promise.resolve = function (val) {
  return new Promise((resolve,reject)=>{
    resolve(val);
  })
}
Promise.reject = function (err) {
  return new Promise((resolve,reject)=>{
    reject(err);
  })
}

all、finally

  • all 执行所有promise 一个错误就返回错误,全部成功返回成功

  • finally 无论正确错误都会执行

Promise.all = function (pList) {
  if (Array.isArray(pList)) throw new Error("传入一个数组");
  let index = 0;
  let dataList = [];
  return new Promise((resolve, reject) => {
    function handle(item, i) {
      dataList[i] = item;
      if (++index === pList.length) resolve(dataList);
    }
    for (let i = 0; i < pList.length; i++) {
      Promise.resolve(pList[i]).then((res) => {
        handle(res, i);
      }, reject);
    }
  });
}
// finally怎么都可以被执行,那就中间利用then方法拦截一下,然后再和then方法处理两个参数函数一样即可
Promise.prototype.finally = function (fn){
  return this.then((value) => {
    return Promise.resolve(fn()).then(() => value);
  }, (err) => {
    return Promise.resolve(fn()).then(() => {throw err});
  })
}

race

返回最先执行完的那个

Promise.prototype.race = function(pList) {
  if(Array.isArray(pList)) throw new TypeError('参数需要是个数组');
  return new Promise((resolve,reject) => {
    for(let i = 0; i < pList.length; i++){
      if(pList[i] instanceof Promise || typeof pList[i].then === "function"){
        pList[i].then(resolve,reject);
      }else {
        resolve(i);
      }
    }
  })
}

中断promise

利用promise的pending效果,return new Promise(()) , 因为没有调用resolvereject,promise处于等待状态因此可以中断