简单实现一个 Promise

Posted 彭博

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单实现一个 Promise相关的知识,希望对你有一定的参考价值。

如何实现一个 Promise/A+

Promise 应该具备什么属性?

  • A promise must be in one of three states: pending, fulfilled, or rejected[1].
  • 同时根据 promise 的三种状态,需要有两个属性用于存储 promise 的返回结果,value, reason
  • Promise 实例是一个 thenable 的对象,即必须包含一个 then 方法

    const PENDING = \'pending\'
    const FULLFILLED = \'fulfilled\'
    const REJECTED = \'rejected\'
    
    class PbPromise{
      constructor(){
        this.state = PENDING
        this.value = undefined
        this.reason = null
      }
    
      then(){
    
      }
    }

如何实例化一个 Promise 实例

  • 通过 new Promise(fn)实例化一个Promise 实例,其中 fn 用于改变当前Promsise的状态,即 Promsie实例在实例化时需要接受一个函数类型的参数 ,executor,这个函数用于改变当前Promise 的状态。该函数是在promise 实例化过程中同步执行的。
  • 外部的函数如何改变 Promsie 内部的状态?这需要 Promise与该函数产生交互,即需要Promise 提供改变其状态的方法给 executor,由于Promise 会有两种状态的变化,因此该方法需要接受两个方法分别处理不同的状态 - executor(resolve, reject)
  const PENDING = \'pending\'
  const FULLFILLED = \'fulfilled\'
  const REJECTED = \'rejected\'
  class PbPromise{ 

    constructor(executor){
      this.state = PENDING
      this.value = undefined
      this.reason = null
+      // 用于将当前 Promise 状态转换为 FULLFILLED
+      const resolve = () => {
+        // 状态只能转变一次
+        if(this.state !== PENDING) return 
+        this.state = FULLFILLED
+      }
+      // 用于将当前 Promise 状态转换为 REJECTED
+      const reject = () => {
+        // 状态只能转变一次
+        if(this.state !== PENDING) return 
+        this.state = REJECTED
+      }
+      // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
+      executor(resolve, reject)
    }

    then(){
    }
  }

then 方法进行回调接收

  • Promse 通过 then来预设状态(state)发生变化之后的回调函数。then 接受两个函数作为参数:promise.then(onFulfilled, onRejected),其中onFulfilled为fulfilled状态的回调,onRejected为rejected之后的回调。
  const PENDING = \'pending\'
  const FULLFILLED = \'fulfilled\'
  const REJECTED = \'rejected\'

  class PbPromise{
    constructor(executor){
      this.state = PENDING
      this.value = undefined
      this.reason = null
+        this.onFulfilledList = []
+        this.onRejectedList = []

      // 用于将当前 Promise 状态转换为 FULLFILLED
      const resolve = value => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = FULLFILLED
        this.value = value

+          // 状态发生变化 要 回调 then 存储的回调函数
+          this.onFulfilledList.forEach(f => f())
      }

      // 用于将当前 Promise 状态转换为 REJECTED
      const reject = reason => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = REJECTED
        this.reason = reason

+          // 状态发生变化 要 回调 then 存储的回调函数
+          this.onRejectedList.forEach(f => f())
      }

      // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
      executor(resolve, reject)
    }

-      then(){
-      }
+      // 接受回调函数,并进行执行 / 存储
+      then(onFulfilled, onRejected){
+        // 检查当前状态,非 PENDING 直接执行回调函数
+        if(this.state === FULLFILLED){
+          return onFulfilled(this.value)
+        }
+        if(this.state === REJECTED){
+          return onRejected(this.reason)
+        }
+
+        // PENDING 状态需要将回调函数存储起来,此时需要两个列表来存储回调函数
+        // onFulfilledList onRejectedList
+        // 这些回调函数应该在 resolve 方法 或者 reject 方法调用之后立即调用
+        this.onFulfilledList.push(onFulfilled)
+        this.onRejectedList.push(onRejected)
    }
  }
  • then 方法要返回一个新的Promise,用于实现链式调用,新的Promsie的状态要在当前then中传入的回调函数执行时变化,用于保证 then 之间回调函数的执行顺序
  class PbPromise{
    constructor(executor){
      this.state = PENDING
      this.value = undefined
      this.reason = null
      this.onFulfilledList = []
      this.onRejectedList = []

      // 用于将当前 Promise 状态转换为 FULLFILLED
      const resolve = value => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = FULLFILLED
        this.value = value

        // 状态发生变化 要 回调 then 存储的回调函数
        this.onFulfilledList.forEach(f => f())
      }

      // 用于将当前 Promise 状态转换为 REJECTED
      const reject = reason => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = REJECTED
        this.reason = reason

        // 状态发生变化 要 回调 then 存储的回调函数
        this.onRejectedList.forEach(f => f())
      }

      // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
      executor(resolve, reject)
    }

    // 接受回调函数,并进行执行 / 存储
    then(onFulfilled, onRejected){
+        return new PbPromise((resolve, reject) => {
        // 检查当前状态,非 PENDING 直接执行回调函数
        if(this.state === FULLFILLED){
-          return onFulfilled(this.value)
+          const value = onFulfilled(this.value)
+          return resolve(value)
        }
        if(this.state === REJECTED){
-            return onRejected(this.reason)
+            const reason = onRejected(this.reason)
+            // 注意这里是 resove
+            return resolve(reason)
        }

        // PENDING 状态需要将回调函数存储起来,此时需要两个列表来存储回调函数
        // onFulfilledList onRejectedList
        // 这些回调函数应该在 resolve 方法 或者 reject 方法调用之后立即调用
-         this.onFulfilledList.push(onFulfilled)
-         this.onRejectedList.push(onRejected)
+          this.onFulfilledList.push(() => {
+              const value = onFulfilled(this.value)
+              resolve(value)
+            })
+          this.onRejectedList.push(() => {
+              const reason = onRejected(this.reason)
+              // 注意这里是 resove
+              resolve(reason)
+            })
+        })
    }
  }

如何处理 返回值为 Promise 的场景?

  • 当当前Promise 的返回值 仍然是一个 Promise 时,当前 then 方法仍然要将返回的Promise的值返回,即要保证 then 中的回调函数拿到的结果不是一个 thenable 的数据
  class PbPromise{
    constructor(executor){
      this.state = PENDING
      this.value = undefined
      this.reason = null
      this.onFulfilledList = []
      this.onRejectedList = []

      // 用于将当前 Promise 状态转换为 FULLFILLED
      const resolve = value => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = FULLFILLED

        this.value = value      
        // 状态发生变化 要 回调 then 存储的回调函数
        this.onFulfilledList.forEach(f => f())
      }

      // 用于将当前 Promise 状态转换为 REJECTED
      const reject = reason => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = REJECTED

        this.reason = reason
        // 状态发生变化 要 回调 then 存储的回调函数
        this.onRejectedList.forEach(f => f())
        
      }

      // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
      executor(resolve, reject)
    }

    // 接受回调函数,并进行执行 / 存储
    then(onFulfilled, onRejected){
      return new PbPromise((resolve, reject) => {
        // 检查当前状态,非 PENDING 直接执行回调函数
        if(this.state === FULLFILLED){
          const value = onFulfilled(this.value)
+            // 处理返回值为 Promise 的场景
+            if(typeof value === \'object\' && typeof value.then === \'function\') {
+              return value.then(resolve, reject)
+            }
          return resolve(value)
        }
        if(this.state === REJECTED){
          const reason = onRejected(this.reason)
+            // 处理返回值为 Promise 的场景
+            if(typeof reason === \'object\' && typeof reason.then === \'function\') {
+              return reason.then(resolve, reject)
+            }
          return resolve(reason)
        }

        // PENDING 状态需要将回调函数存储起来,此时需要两个列表来存储回调函数
        // onFulfilledList onRejectedList
        // 这些回调函数应该在 resolve 方法 或者 reject 方法调用之后立即调用
        this.onFulfilledList.push(() => {
            const value = onFulfilled(this.value)
+              // 处理返回值为 Promise 的场景
+              if(typeof value === \'object\' && typeof value.then === \'function\') {
+                return value.then(resolve, reject)
+              }
           resolve(value)
          })
        this.onRejectedList.push(() => {
            const reason = onRejected(this.reason)
+              // 处理返回值为 Promise 的场景
+              if(typeof reason === \'object\' && typeof reason.then === \'function\') {
+                return reason.then(resolve, reject)
+              }
            resolve(reason)
          })
      })
    }
  }

代码异常的 catch

  • 通过 try catch 将异常捕获并抛出
    class PbPromise{
      constructor(executor){
        this.state = PENDING
        this.value = undefined
        this.reason = null
        this.onFulfilledList = []
        this.onRejectedList = []

        // 用于将当前 Promise 状态转换为 FULLFILLED
        const resolve = value => {
          // 状态只能转变一次
          if(this.state !== PENDING) return 
          this.state = FULLFILLED

          this.value = value      
          // 状态发生变化 要 回调 then 存储的回调函数
          this.onFulfilledList.forEach(f => f())
        }

        // 用于将当前 Promise 状态转换为 REJECTED
        const reject = reason => {
          // 状态只能转变一次
          if(this.state !== PENDING) return 
          this.state = REJECTED

          this.reason = reason
          // 状态发生变化 要 回调 then 存储的回调函数
          this.onRejectedList.forEach(f => f())
          
        }

        // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
        executor(resolve, reject)
      }

      // 接受回调函数,并进行执行 / 存储
      then(onFulfilled, onRejected){
        return new PbPromise((resolve, reject) => {
          // 检查当前状态,非 PENDING 直接执行回调函数
          if(this.state === FULLFILLED){
+            try {
              const value = onFulfilled(this.value)
              // 处理返回值为 Promise 的场景
              if(typeof value === \'object\' && typeof value.then === \'function\') {
                return value.then(resolve, reject)
              }
              return resolve(value)
+            }catch(e){
+              return reject(e)
+            }
          }
          if(this.state === REJECTED){
+            try{
              const reason = onRejected(this.reason)
              // 处理返回值为 Promise 的场景
              if(typeof reason === \'object\' && typeof reason.then === \'function\') {
                return reason.then(resolve, reject)
              }
              return resolve(reason)
+            }catch(e){
+              return reject(reason)
+            }
          }

          // PENDING 状态需要将回调函数存储起来,此时需要两个列表来存储回调函数
          // onFulfilledList onRejectedList
          // 这些回调函数应该在 resolve 方法 或者 reject 方法调用之后立即调用
          this.onFulfilledList.push(() => {
+              try{
                const value = onFulfilled(this.value)
                // 处理返回值为 Promise 的场景
                if(typeof value === \'object\' && typeof value.then === \'function\') {
                  return value.then(resolve, reject)
                }
                resolve(value)
+              }catch(e){
+                reject(e)
+              }
            })
          this.onRejectedList.push(() => {
+              try{
                const reason = onRejected(this.reason)
                // 处理返回值为 Promise 的场景
                if(typeof reason === \'object\' && typeof reason.then === \'function\') {
                  return reason.then(resolve, reject)
                }
                resolve(reason)
+             }catch(e){
+               reject(e)
+             }
            })
        })
      }
    }

代码化简

  • 抽离then 方法中调用回调函数的逻辑
  • 抽离 try catch
  class PbPromise{
    constructor(executor){
      this.state = PENDING
      this.value = undefined
      this.reason = null
      this.onFulfilledList = []
      this.onRejectedList = []

      // 用于将当前 Promise 状态转换为 FULLFILLED
      const resolve = value => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = FULLFILLED

        this.value = value      
        // 状态发生变化 要 回调 then 存储的回调函数
        this.onFulfilledList.forEach(f => f())
      }

      // 用于将当前 Promise 状态转换为 REJECTED
      const reject = reason => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = REJECTED

        this.reason = reason
        // 状态发生变化 要 回调 then 存储的回调函数
        this.onRejectedList.forEach(f => f())
        
      }
      // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
      executor(resolve, reject)
    }

    // 接受回调函数,并进行执行 / 存储
    then(onFulfilled, onRejected){
      return new PbPromise((resolve, reject) => {
        // 检查当前状态,非 PENDING 直接执行回调函数
        if(this.state === FULLFILLED){
-            try {
-              const value = onFulfilled(this.value)
-              // 处理返回值为 Promise 的场景
-              if(typeof value === \'object\' && typeof value.then === \'function\') {
-                return value.then(resolve, reject)
-              }
-              return resolve(value)
-            }catch(e){
-              return reject(e)
-            }
+            return this.tryCatchFn(this.handleResolve(onFulfilled, resolve, reject), reject)()
        }
        if(this.state === REJECTED){
-            try{
-              const reason = onRejected(this.reason)
-              // 处理返回值为 Promise 的场景
-              if(typeof reason === \'object\' && typeof reason.then === \'function\') {
-                return reason.then(resolve, reject)
-              }
-              return resolve(reason)
-            }catch(e){
-              return reject(reason)
-            }
+            return this.tryCatchFn(this.handleReject(onRejected, resolve, reject), reject)()
        }

        // PENDING 状态需要将回调函数存储起来,此时需要两个列表来存储回调函数
        // onFulfilledList onRejectedList
        // 这些回调函数应该在 resolve 方法 或者 reject 方法调用之后立即调用
-          this.onFulfilledList.push(() => {
-              try{
-                const value = onFulfilled(this.value)
-                // 处理返回值为 Promise 的场景
-                if(typeof value === \'object\' && typeof value.then === \'function\') {
-                  return value.then(resolve, reject)
-                }
-                resolve(value)
-              }catch(e){
-                reject(e)
-              }
-            })
-          this.onRejectedList.push(() => {
-              try{
-                const reason = onRejected(this.reason)
-                // 处理返回值为 Promise 的场景
-                if(typeof reason === \'object\' && typeof reason.then === \'function\') {
-                  return reason.then(resolve, reject)
-                }
-                resolve(reason)
-             }catch(e){
-               reject(e)
-             }
-            })
+          this.onFulfilledList.push(this.tryCatchFn(this.handleResolve(onFulfilled, resolve, reject), reject))
+          this.onRejectedList.push(this.tryCatchFn(this.handleReject(onRejected, resolve, reject), reject))
      })
    }

+      handleResolve(onFulfilled, resolve, reject){
+        return () => {
+          const value = onFulfilled(this.value)
+          // 处理返回值为 Promise 的场景
+          if(typeof value === \'object\' && typeof value.then === \'function\') {
+            return value.then(resolve, reject)
+          }
+          resolve(value)
+        }
+      }

+      handleReject(onRejected, resolve, reject){
+        return () => {
+          const reason = onRejected(this.reason)
+          // 处理返回值为 Promise 的场景
+          if(typeof reason === \'object\' && typeof reason.then === \'function\') {
+            return reason.then(resolve, reject)
+          }
+          resolve(reason)
+        }
+      }

+      tryCatchFn(fn, catchFn){
+        return () => {
+          try {
+            fn()
+          }catch (e){
+            catchFn(e)
+          }
+        }
+      }
  }

then 方法中的回调函数必须是异步执行的

  • then方法中回调函数的异步执行,可以通过 setTimeout setImmediate,MutationObserver, process.nextTick 来进行模拟,注意这里只是进行异步调用的模拟,并不是说原生的 promise就是通过以上方式实现。因为涉及到微任务与宏任务之间的执行顺序,这里并不能完全的模拟实现
  class PbPromise{
    constructor(executor){
      this.state = PENDING
      this.value = undefined
      this.reason = null
      this.onFulfilledList = []
      this.onRejectedList = []

      // 用于将当前 Promise 状态转换为 FULLFILLED
      const resolve = value => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = FULLFILLED

        this.value = value      
        // 状态发生变化 要 回调 then 存储的回调函数
        this.onFulfilledList.forEach(f => f())
      }

      // 用于将当前 Promise 状态转换为 REJECTED
      const reject = reason => {
        // 状态只能转变一次
        if(this.state !== PENDING) return 
        this.state = REJECTED

        this.reason = reason
        // 状态发生变化 要 回调 then 存储的回调函数
        this.onRejectedList.forEach(f => f())
        
      }
      // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
      executor(resolve, reject)
    }

    // 接受回调函数,并进行执行 / 存储
    then(onFulfilled, onRejected){
      return new PbPromise((resolve, reject) => {
        // 检查当前状态,非 PENDING 直接执行回调函数
        if(this.state === FULLFILLED){
-            return this.tryCatchFn(this.handleResolve(onFulfilled, resolve, reject), reject)()
+            // 通过 setTimeout 来模拟实现异步调用
+            setTimeout(this.tryCatchFn(this.handleResolve(onFulfilled, resolve, reject), reject))
        }
        if(this.state === REJECTED){
-            return this.tryCatchFn(this.handleReject(onRejected, resolve, reject), reject)()
+            // 通过 setTimeout 来模拟实现异步调用
+            setTimeout(this.tryCatchFn(this.handleReject(onRejected, resolve, reject), reject))
        }

        // PENDING 状态需要将回调函数存储起来,此时需要两个列表来存储回调函数
        // onFulfilledList onRejectedList
        // 这些回调函数应该在 resolve 方法 或者 reject 方法调用之后立即调用
        this.onFulfilledList.push(this.tryCatchFn(this.handleResolve(onFulfilled, resolve, reject), reject))
        this.onRejectedList.push(this.tryCatchFn(this.handleReject(onRejected, resolve, reject), reject))
      })
    }

    handleResolve(onFulfilled, resolve, reject){
      return () => {
        const value = onFulfilled(this.value)
        // 处理返回值为 Promise 的场景
        if(typeof value === \'object\' && typeof value.then === \'function\') {
          return value.then(resolve, reject)
        }
        resolve(value)
      }
    }

    handleReject(onRejected, resolve, reject){
      return () => {
        const reason = onRejected(this.reason)
        // 处理返回值为 Promise 的场景
        if(typeof reason === \'object\' && typeof reason.then === \'function\') {
          return reason.then(resolve, reject)
        }
        resolve(reason)
      }
    }

    tryCatchFn(fn, catchFn){
      return () => {
        try {
          fn()
        }catch (e){
          catchFn(e)
        }
      }
    }
  }

onRejected, onFulfilled 可选 以及 reject 穿透

  • onRejected, onFulfilled 两个回调函数是可选的,因此当前 promise 返回的数据 resolve 或者reject 之后,value, reason 应该能够传递给接下来的 onFulfilled, onRejected,方法进行处理。
+ const NOOP = () => {}

class PbPromise{
  constructor(executor){
    this.state = PENDING
    this.value = undefined
    this.reason = null
    this.onFulfilledList = []
    this.onRejectedList = []

    // 用于将当前 Promise 状态转换为 FULLFILLED
    const resolve = value => {
      // 状态只能转变一次
      if(this.state !== PENDING) return 
      this.state = FULLFILLED

      this.value = value      
      // 状态发生变化 要 回调 then 存储的回调函数
      this.onFulfilledList.forEach(f => f())
    }

    // 用于将当前 Promise 状态转换为 REJECTED
    const reject = reason => {
      // 状态只能转变一次
      if(this.state !== PENDING) return 
      this.state = REJECTED

      this.reason = reason
      // 状态发生变化 要 回调 then 存储的回调函数
      this.onRejectedList.forEach(f => f())
      
    }
    // 此时外部通过 调用 resolve 或者 reject 就可以改变 Promise 内部的状态
    executor(resolve, reject)
  }

  // 接受回调函数,并进行执行 / 存储
-  then(onFulfilled, onRejected){
+  then(onFulfilled = NOOP, onRejected = NOOP){
+    if(typeof onFulfilled !== \'function\') onFulfilled = NOOP;
+    if(typeof onRejected !== \'function\') onRejected = NOOP;
    return new PbPromise((resolve, reject) => {
      // 检查当前状态,非 PENDING 直接执行回调函数
      if(this.state === FULLFILLED){
        // 通过 setTimeout 来模拟实现异步调用
        setTimeout(this.tryCatchFn(this.handleResolve(onFulfilled, resolve, reject), reject))
      }
      if(this.state === REJECTED){
        // 通过 setTimeout 来模拟实现异步调用
        setTimeout(this.tryCatchFn(this.handleReject(onRejected, resolve, reject), reject))
      }

      // PENDING 状态需要将回调函数存储起来,此时需要两个列表来存储回调函数
      // onFulfilledList onRejectedList
      // 这些回调函数应该在 resolve 方法 或者 reject 方法调用之后立即调用
      this.onFulfilledList.push(this.tryCatchFn(this.handleResolve(onFulfilled, resolve, reject), reject))
      this.onRejectedList.push(this.tryCatchFn(this.handleReject(onRejected, resolve, reject), reject))
    })
  }

  handleResolve(onFulfilled, resolve, reject){
    return () => {
+      // resolve 穿透
+      if(onFulfilled === NOOP) return resolve(this.value)
      const value = onFulfilled(this.value)
      // 处理返回值为 Promise 的场景
      if(typeof value === \'object\' && typeof value.then === \'function\') {
        return value.then(resolve, reject)
      }
      resolve(value)
    }
  }

  handleReject(onRejected, resolve, reject){
    return () => {
+      // reject 穿透
+      if(onRejected === NOOP) return reject(this.reason)
      const reason = onRejected(this.reason)
      // 处理返回值为 Promise 的场景
      if(typeof reason === \'object\' && typeof reason.then === \'function\') {
        return reason.then(resolve, reject)
      }
      resolve(reason)
    }
  }

  tryCatchFn(fn, catchFn){
    return () => {
      try {
        fn()
      }catch (e){
        catchFn(e)
      }
    }
  }
}

其他

  • 这里仅仅是对Promise 的一种简单粗略的实现,
  • 值得注意的是[1]中有说明 promise 在事件循环中的位置可能会根据实现有所不同
[1] Promise/A+ https://promisesaplus.com
[2] https://promisesaplus.com/#notes

以上是关于简单实现一个 Promise的主要内容,如果未能解决你的问题,请参考以下文章

前端面试题之手写promise

Promise A 规范的一个简单的浏览器端实现

简单实现异步编程promise模式

简单实现一个 Promise

Promise简单实现--摘抄

实现简单 promise