Javascript Promise 手写 优缺点

121 阅读6分钟

1. promise有哪些状态?对应值有哪些?

  • promise

    • pending、fulfilled、rejected
  • executor

    • new Promise的时候立即执行

    • 回调接收参数:resolve + reject

2. promise的默认状态是?状态如何流转?

  • 默认状态:pending

  • 内部维护成功value:undefined、thenable、promise

  • 内部维护失败变量reason

  • promise状态流转

    • pending => rejected

    • pending => fulfilled

3. promise的返回值?

  • then方法:接收回调 onFulfilled、onRejected

  • 如果then时

    • promise已经成功

      • 执行onFulfilled,参数value
    • promise已经失败

      • 执行onRejected,参数是reason
    • 有异常传递onRejected

4. 手写promise - 基本同步版本

  • 设置三种状态 PENDING FULFILLED REJECTED

  • 创建一个 Promise 类

    • 从外部拿到执行器

      • 默认状态

      • 成功失败的值

      • 成功回调 resolve - value

        • 判断状态为 pending 则单向流转

          • 状态

      • 失败回调

        • 判断状态为 pending 则单向流转
      • try catch 执行第一波宏任务

        • 执行 成功和失败的回调

          • catch

            • 执行失败的回调
    • then 传值 onFulfilled onRejected

      • 判断状态是 FULFILLED

        • onFulfilled 值
      • 判断状态是 REJECTED

        • onRejected 原因
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class Promise {
  // 从外部拿到要执行的执行器
  constructor(executor) {
    // 1. 默认状态 - PENDING
    this.status = PENDING
    // 2. 维护内部成功失败的值
    this.value = undefined
    this.reason = undefined

    // 成功回调
    let resolve = (value) => {
      // 单向流转
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
      }
    }

    // 失败的回调
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = reason
      }
    }
    // 执行第一波宏任务
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    }

    if (this.status === REJECTED) {
      onRejected(this.reason)
    }
  }
}

完整版

class MyPromise {
  // 构造方法
  constructor(executor) {
    // 初始化值
    this.initValue()
    // 初始化this指向
    this.initBind()
    try {
      // 执行传进来的函数
      executor(this.resolve, this.reject)
    } catch (e) {
      // 捕捉到错误直接执行reject
      this.reject(e)
    }
  }

  initBind() {
    // 初始化this
    this.resolve = this.resolve.bind(this)
    this.reject = this.reject.bind(this)
  }

  initValue() {
    // 初始化值
    this.PromiseResult = null // 终值
    this.PromiseState = 'pending' // 状态
    this.onFulfilledCallbacks = [] // 保存成功回调
    this.onRejectedCallbacks = [] // 保存失败回调
  }

  resolve(value) {
    // state是不可变的
    if (this.PromiseState !== 'pending') return
    // 如果执行resolve,状态变为fulfilled
    this.PromiseState = 'fulfilled'
    // 终值为传进来的值
    this.PromiseResult = value
    // 执行保存的成功回调
    while (this.onFulfilledCallbacks.length) {
      this.onFulfilledCallbacks.shift()(this.PromiseResult)
    }
  }

  reject(reason) {
    // state是不可变的
    if (this.PromiseState !== 'pending') return
    // 如果执行reject,状态变为rejected
    this.PromiseState = 'rejected'
    // 终值为传进来的reason
    this.PromiseResult = reason
    // 执行保存的失败回调
    while (this.onRejectedCallbacks.length) {
      this.onRejectedCallbacks.shift()(this.PromiseResult)
    }
  }

  then(onFulfilled, onRejected) {
    // 接收两个回调 onFulfilled, onRejected

    // 参数校验,确保一定是函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (val) => val
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }

    var thenPromise = new MyPromise((resolve, reject) => {
      const resolvePromise = (cb) => {
        setTimeout(() => {
          try {
            const x = cb(this.PromiseResult)
            if (x === thenPromise) {
              // 不能返回自身哦
              throw new Error('不能返回自身。。。')
            }
            if (x instanceof MyPromise) {
              // 如果返回值是Promise
              // 如果返回值是promise对象,返回值为成功,新promise就是成功
              // 如果返回值是promise对象,返回值为失败,新promise就是失败
              // 谁知道返回的promise是失败成功?只有then知道
              x.then(resolve, reject)
            } else {
              // 非Promise就直接成功
              resolve(x)
            }
          } catch (err) {
            // 处理报错
            reject(err)
            throw new Error(err)
          }
        })
      }

      if (this.PromiseState === 'fulfilled') {
        // 如果当前为成功状态,执行第一个回调
        resolvePromise(onFulfilled)
      } else if (this.PromiseState === 'rejected') {
        // 如果当前为失败状态,执行第二个回调
        resolvePromise(onRejected)
      } else if (this.PromiseState === 'pending') {
        // 如果状态为待定状态,暂时保存两个回调
        // 如果状态为待定状态,暂时保存两个回调
        this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
        this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
      }
    })

    // 返回这个包装的Promise
    return thenPromise
  }
}
  • 大厂专供版

    const PENDING = "pending"; 
    const FULFILLED = "fulfilled"; 
    const REJECTED = "rejected";
    const resolvePromise = (promise, x, resolve, reject) => {
      if (x === promise) {
        // If promise and x refer to the same object, reject promise with a TypeError as the reason.
        reject(new TypeError('循环引用'))
      }
      // if x is an object or function,
      if (x !== null && typeof x === 'object' || typeof x === 'function') {
        // If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
        let called
        try { // If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
          let then = x.then // Let then be x.then
          // If then is a function, call it with x as this
          if (typeof then === 'function') {
            // If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
            // If/when rejectPromise is called with a reason r, reject promise with r.
            then.call(x, y => {
              if (called) return
              called = true
              resolvePromise(promise, y, resolve, reject)
            }, r => {
              if (called) return
              called = true
              reject(r)
            })
          } else {
            // If then is not a function, fulfill promise with x.
            resolve(x)
          }
        } catch (e) {
          if (called) return
          called = true
          reject(e)
        }
      } else {
        // If x is not an object or function, fulfill promise with x
        resolve(x)
      }
    }
    function Promise(excutor) {
      let that = this; // 缓存当前promise实例例对象
      that.status = PENDING; // 初始状态
      that.value = undefined; // fulfilled状态时 返回的信息
      that.reason = undefined; // rejected状态时 拒绝的原因 
      that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
      that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
      function resolve(value) { // value成功态时接收的终值
        if(value instanceof Promise) {
          return value.then(resolve, reject);
        }
        // 实践中要确保 onFulfilled 和 onRejected ⽅方法异步执⾏行行,且应该在 then ⽅方法被调⽤用的那⼀一轮事件循环之后的新执⾏行行栈中执⾏行行。
        setTimeout(() => {
          // 调⽤用resolve 回调对应onFulfilled函数
          if (that.status === PENDING) {
            // 只能由pending状态 => fulfilled状态 (避免调⽤用多次resolve reject)
            that.status = FULFILLED;
            that.value = value;
            that.onFulfilledCallbacks.forEach(cb => cb(that.value));
          }
        });
      }
      function reject(reason) { // reason失败态时接收的拒因
        setTimeout(() => {
          // 调⽤用reject 回调对应onRejected函数
          if (that.status === PENDING) {
            // 只能由pending状态 => rejected状态 (避免调⽤用多次resolve reject)
            that.status = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach(cb => cb(that.reason));
          }
        });
      }
    
      // 捕获在excutor执⾏行行器器中抛出的异常
      // new Promise((resolve, reject) => {
      //     throw new Error('error in excutor')
      // })
      try {
        excutor(resolve, reject);
      } catch (e) {
        reject(e);
      }
    }
    Promise.prototype.then = function(onFulfilled, onRejected) {
      const that = this;
      let newPromise;
      // 处理理参数默认值 保证参数后续能够继续执⾏行行
      onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
      onRejected = typeof onRejected === "function" ? onRejected : reason => {
        throw reason;
      };
      if (that.status === FULFILLED) { // 成功态
        return newPromise = new Promise((resolve, reject) => {
          setTimeout(() => {
            try{
              let x = onFulfilled(that.value);
              resolvePromise(newPromise, x, resolve, reject); //新的promise resolve 上⼀一个onFulfilled的返回值
            } catch(e) {
              reject(e); // 捕获前⾯面onFulfilled中抛出的异常then(onFulfilled, onRejected);
            }
          });
        })
      }
      if (that.status === REJECTED) { // 失败态
        return newPromise = new Promise((resolve, reject) => {
          setTimeout(() => {
            try {
              let x = onRejected(that.reason);
              resolvePromise(newPromise, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          });
        });
      }
      if (that.status === PENDING) { // 等待态
    // 当异步调⽤用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
        return newPromise = new Promise((resolve, reject) => {
          that.onFulfilledCallbacks.push((value) => {
            try {
              let x = onFulfilled(value);
              resolvePromise(newPromise, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          });
          that.onRejectedCallbacks.push((reason) => {
            try {
              let x = onRejected(reason);
              resolvePromise(newPromise, x, resolve, reject);
            } catch(e) {
              reject(e);
            }
          });
        });
      }
    };
    

优缺点

优点

  • 统一异步API,
  • 让回调函数变成了规范的链式写法

    • 解决回调地狱
  • 有一整套接口 => 实现许多强大的功能

    • 同时执行多个异步操作,等其状态都改变以后,再执行一个回调函数

    • 为多个回调函数中抛出的错误,更好的异常处理方式

  • 状态一旦改变,无论何时查询,都能得到这个状态

    • 无论何时为peomise实例添加回调函数,该函数都能正确执行
  • 传统写法的话都通过监听事件来执行回调函数,一旦错过了事件,再添加回调函数则不会执行

缺点

  • 一旦开始执行就无法取消

  • 无法进度追踪

  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部

  • Promise 真正执行回调的时候,定义 Promise 那部分实际上已经走完了,所以 Promise 的报错堆栈上下文不太友好