首页 > 其他分享 >Promise 的模拟实现

Promise 的模拟实现

时间:2022-11-11 16:45:34浏览次数:48  
标签:resolve const 实现 state Promise result reject 模拟

1. 为什么会有 Promise

当我们多次进行有依赖的网络请求或者文件请求时, 很可能会造成代码的层层嵌套, 导致回调地狱的出现:

$.ajax({
  url: "xxx",
  success: function(result) {
    $.ajax({
      url: result,
      success: function(result) {
        $.ajax({
          url: result,
          success: function(result) {
            //...
          }
        })
      }
    })
  }
})

n 重依赖的请求会导致出现 n 重嵌套, 为了解决回调地狱问题, JS 中有了 Promise

2. 如何实现 Promise

2.1 初步实现 Promise

  • 为什么会有回调地狱的问题出现呢?
    一个重要的原因就是请求所获得的结果只会出现在内层的函数中, 故而要使用该结果就必须得在请求中套用请求, 最终导致回调地狱的出现

  • 故而, 解决回调地狱的第一步就是将请求所获取的结果从嵌套里提出来:
    于是我们联想到 JQuery 的执行过程--将每次执行所获取的结果存储在 JQuery 实例对象的某一属性上. 这不仅将结果提取了出来, 还能够实现链式调用, 从而方便使用

class Promise {
  constructor(executor) { // new Promise 时要传入一个有两个形参的函数
    this.result = undefined // 设置实例对象的结果
    
    const resolve = result => { // 当请求成功, 获得结果时, 调用 resolve, 设置结果到实例对象上
      this.result = result
    }
    const reject = reason => { // 当请求失败时, 调用 reject, 将失败的原因记录到实例对象上
      this.result = reason
    }

    executor(resolve, reject) // 执行传入的函数
  }
    
  then(onFulfilled, onRejected) { // 对于 then 方法的调用, 传入两个函数, 分别对成功和失败的两种情况进行处理
    // ...
  }
}

2.2 初步实现 then

我们容易发现, 在对 then 方法的编写中, 我们无从得知上次调用所获取的结果是成功的还是失败的, 为了解决这个问题, 想到两种方法:

  1. 额外设置一个属性 state 来表示成功或失败的状态
  2. 额外设置一个属性 reason 用来记录失败状态对应的结果(result 用来表示成功时所对应的结果), 当为一个属性赋值时, 就要清空另一个属性的值

我们暂且先使用第一种方法来完成对成功和失败状态的判断

class Promise {
  constructor(executor) { // new Promise 时要传入一个有两个形参的函数
    this.state = "pending" // 设置实例对象所对应的状态的
    this.result = undefined // 设置实例对象的结果

    const resolve = result => { // 改变状态和结果(当成功时调用)
      this.state = "fulfilled"
      this.result = result
    }
    const reject = reason => { // 改变状态和结果(当失败时调用)
      this.state = "rejected"
      this.result = reason
    }

    executor(resolve, reject) // 执行传入的函数
  }

  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => { // 保证链式调用
      if (this.state === "pending") {

      } else if (this.state === "fulfilled") {
        onFulfilled(this.result, resolve, reject)
      } else if (this.state === "rejected") {
        onFulfilled(this.result, resolve, reject)
      }
    })
  }
}

接着, 我们就发现我们面临着两个问题:

  1. 如果调用 then 时, Promise 实例对象的状态还没有确定怎么办?
    • 我们可以先将传入 then 中的方法存储到实例对象上, 等到实例对象的状态确定后, 再执行相应方法
  2. 如果 onFulfilledonRejected 执行所获得的结果为 Promise 实例对象怎么办?
    • 这乍一看好像并不是什么问题, 如果 p1.then 中的方法返回的是 Promise 实例对象 p2 的话, 就把 p2 赋值给 p1.result 不就行了吗
    • 但这样是不行的. 假设在对实例对象 p1 进行处理时, 相应的函数返回了实例对象 p2, 那就会形成 p1.result === p2 的情况, 如果要获取 p2 的值, 则会出现嵌套的情况, 而嵌套过多的话, 最终就会形成回调地狱
      p1.then(fn1, fn2) // p1(p2)
        .then(p2 => {
          p2.then(p3 => {
            p3.then(p4 => {
              // ...
            })
          })
        })
      

2.3 初步实现 resolvePromise

那么如何解决这个问题呢?
既然这个问题出现的原因就是 onFulfilledonRejected 获取的结果是 Promise 实例对象导致的, 那我们是不是可以处理一下这个 Promise 实例对象, 直接获取其中的结果呢(若是 onFulfilled/onRejected 所获取的 result 无法返回, 那么直接返回 new Promise 并在其中进行 resolve/reject 处理即可)

function resolvePromise(x, resolve, reject) {
  if (typeof x === "object" && x != null || typeof x === "function") { // x 为引用类型值
    const then = x.then // 若 x 为 Promise 实例对象, 则其一定会有 then 方法
    if (typeof then === "function") {
      then.call(x, result => { // 若 x 的状态为成功, 则其对应的结果有可能会是 Promise 实例对象, 所以还需要继续递归调用 resolvePromise 方法
        resolvePromise(result, resolve, reject)
      }, reason => { // 若 x 的状态为失败, 则其对应的结果不会是 Promise 实例对象, 故而可以直接调用 reject(reason)
        reject(reason)
      })
    } else { // x 没有 then 方法, 不会是 Promise 实例对象
      resolve(x)
    }
  } else { // x 为基础类型值, 不会是 Promise 实例对象
    resolve(x)
  }
}
then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => { // 保证链式调用
      if (this.state === "pending") {
        this.onFulfilledList.push(() => {
          const x = onFulfilled(this.result)
          resolvePromise(x, resolve, reject)
        })
        this.onRejectedList.push(() => {
          const x = onRejected(this.result)
          resolvePromise(x, resolve, reject)
        })
      } else if (this.state === "fulfilled") { // 当调用对象的状态为fulfilled时
        const x = onFulfilled(this.result)
        resolvePromise(x, resolve, reject)
      } else if (this.state === "rejected") { // 当调用对象的状态为rejected时
        const x = onFulfilled(this.result)
        resolvePromise(x, resolve, reject)
      }
    })
  }
constructor(executor) { // new Promise 时要传入一个有两个形参的函数
  this.state = "pending" // 设置实例对象所对应的状态的
  this.result = undefined // 设置实例对象的结果
  this.onFulfilledList = []
  this.onRejectedList = []
  /* 由于可能会出现一个 Promise 实例对象对应多个 then 方法的情况, 所以要将存储方法的属性设置为数组
  * let p1 = new Promise((resolve, reject) => {setTimeout(resolve(1))})
  * p1.then(onFulfilled1)
  * p1.then(onFulfilled2)
  * */

  const resolve = result => { // 改变状态和结果(当成功时调用)
    this.state = "fulfilled"
    this.result = result
    this.onFulfilledList.forEach(fn => fn())
  }
  const reject = reason => { // 改变状态和结果(当失败时调用)
    this.state = "rejected"
    this.result = reason
    this.onRejectedList.forEach(fn => fn())
  }

  executor(resolve, reject) // 执行传入的函数
}

再对这些方法进行一些加工处理, 就可以得到初版本的 Promise 实现了

2.4 Promise 初版本

class Promise {
  constructor(executor) { // new Promise 时要传入一个有两个形参的函数
    this.state = "pending" // 设置实例对象所对应的状态的
    this.result = undefined // 设置实例对象的结果
    this.onFulfilledList = []
    this.onRejectedList = []
    /* 由于可能会出现一个 Promise 实例对象对应多个 then 方法的情况, 所以要将存储方法的属性设置为数组
    * let p1 = new Promise((resolve, reject) => {setTimeout(resolve(1))})
    * p1.then(onFulfilled1)
    * p1.then(onFulfilled2)
    * */

    const resolve = result => { // 改变状态和结果(当成功时调用)
      if (this.state !== "pending") return
      // 防止多次调用 resolve/reject 从而导致 onFulfilledList/onRejectedList 多次执行
      this.state = "fulfilled"
      this.result = result
      this.onFulfilledList.forEach(fn => fn()) // 当执行 resolve 时, 会将一系列 then 中的 onFulfilled 方法执行
    }
    const reject = reason => { // 改变状态和结果(当失败时调用)
      if (this.state !== "pending") return
      // 防止多次调用 resolve/reject 从而导致 onFulfilledList/onRejectedList 多次执行
      this.state = "rejected"
      this.result = reason
      this.onRejectedList.forEach(fn => fn()) // 当执行 reject 时, 会将一系列 then 中的 onRejected 方法执行
    }
    try { // 当 executor 执行出错时, 获取其出错的原因
      executor(resolve, reject) // 执行传入的函数
    } catch(err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    // 如果 onFulfilled 或 onRejected 并没有传函数进来, 则启用默认函数
    if (typeof onFulfilled !== "function") onFulfilled = result => result
    if (typeof onRejected !== "function") onRejected = reason => {throw reason}

    return new Promise((resolve, reject) => { // 保证链式调用
      if (this.state === "pending") {
        this.onFulfilledList.push(() => {
          try {
            const x = onFulfilled(this.result)
            resolvePromise(x, resolve, reject)
          } catch(err) { // 获取出错原因
            reject(err)
          }
        })
        this.onRejectedList.push(() => {
          try {
            const x = onRejected(this.result)
            resolvePromise(x, resolve, reject)
          } catch(err) {
            reject(err)
          }
        })
      } else if (this.state === "fulfilled") { // 当调用对象的状态为fulfilled时
        try {
          const x = onFulfilled(this.result)
          resolvePromise(x, resolve, reject)
        } catch(err) { // 获取出错原因
          reject(err)
        }
      } else if (this.state === "rejected") { // 当调用对象的状态为rejected时
        try {
          const x = onRejected(this.result)
          resolvePromise(x, resolve, reject)
        } catch(err) {
          reject(err)
        }
      }
    })
  }
}

function resolvePromise(x, resolve, reject) {
  if (typeof x === "object" && x != null || typeof x === "function") { // x 为引用类型值
    const then = x.then // 若 x 为 Promise 实例对象, 则其一定会有 then 方法
    if (typeof then === "function") {
      try {
        then.call(x, result => { // 若 x 的状态为成功, 则其对应的结果有可能会是 Promise 实例对象, 所以还需要继续递归调用 resolvePromise 方法
          resolvePromise(result, resolve, reject)
        }, reason => { // 若 x 的状态为失败, 则其对应的结果不会是 Promise 实例对象, 故而可以直接调用 reject(reason)
          reject(reason)
        })
      } catch(err) {
        reject(err)
      }
    } else { // x 没有 then 方法, 不会是 Promise 实例对象
      resolve(x)
    }
  } else { // x 为基础类型值, 不会是 Promise 实例对象
    resolve(x)
  }
}

这个版本的 Promise 已经能够解决绝大多数的回调地狱的问题

new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log(1000)
    resolve(200)
  }, 1000)
}).then(result => {
  console.log(233)
}).then(result => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(3000)
      resolve(result << 1)
    }, 3000)
  })
}).then(result => console.log(result))
//=> 1000 233 3000 0

2.5 Promise/A+ 的 Promise 实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

const resolvePromise = (promise2, x, resolve, reject) => {
  // 防止出现 let p = promiseObj.then(result => p, reason => p) 的情况
  if (promise2 === x) reject(new TypeError('cycling reference'))

  // 用于区分 onFulfilled/onRejected 返回的 x 是否是引用类型, 
  if (typeof x === 'object' && x != null || typeof x === 'function') {
    // 防止
    let called = false
    try {
      const then = x.then
      // 若 then 为函数, 则认为 x 为 Promise 的实例对象, 通过 then 来改变 promise2 的 result 和 state
      if (typeof then === 'function') {
        then.call(x, result => {
          if (called) return
          called = true
          resolvePromise(promise2, result, resolve, reject)
        }, reason => {
          if (called) return
          called = true
          reject(reason)
        })
      } else { // 若 then 不是函数, 则认为 x 并不是 Promise 实例对象, 将 x 作为 promise2 的 result 来保存
        resolve(x)
      }
    } catch(e) {
      if (called) return
      reject(e)
    }
  } else { // 若 x 不是引用类型, 则其必不是 Promise 实例对象, 则直接调用 resolve 方法, 令 promise2 的 result 为 x
    resolve(x)
  }
}

class Promise {
  constructor(executor) {
    this.state = PENDING
    this.result = undefined

    this.onFulfilledList = []
    this.onRejectedList = []

    const resolve = result => {
      if (this.state !== PENDING) return
      this.state = FULFILLED
      this.result = result

      this.onFulfilledList.forEach(fn => fn())
    }

    const reject = reason => {
      if (this.state !== PENDING) return
      this.state = REJECTED
      this.result = reason
      
      this.onRejectedList.forEach(fn => fn())
    }

    try {
      executor(resolve, reject)
    } catch(e) {
      reject(e)
    }
  }

  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') onFulfilled = result => result
    if (typeof onRejected !== 'function') onRejected = reason => { throw reason }
    
    const promise2 = new Promise((resolve, reject) => {
      if (this.state === PENDING) {
        this.onFulfilledList.push(() => {
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.result)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
        })

        this.onRejectedList.push(() => {
          queueMicrotask(() => {
            try {
              const x = onRejected(this.result)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
        })
      } else if (this.state === FULFILLED) {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.result)
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        })
      } else if (this.state === REJECTED) {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.result)
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        })
      }
    })

    return promise2
  }
}

2.5.1 promises-aplus-tests 测试

Promise.deferred = function() {
  const result = {}
  result.promise = new Promise(function(resolve, reject){
    result.resolve = resolve
    result.reject = reject
  })

  return result
}

module.exports = Promise

3. Promise 的其他方法

3.1 实例方法

3.1.1 Promise.prototype.catch

catch(onRejected) {
  return this.then(_, onRejected)
}

3.1.2 Promise.prototype.finally

finally(fn) {
  return this.then(fn, fn)
}

3.2 静态方法

3.2.1 Promise.resolve

static resolve(value) {
  return new Promise((resolve, reject) => {
    resolve(value)
  })
}

3.2.2 Promise.reject

static reject(reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}

3.2.3 Promise.all

传入的是一个 promise 的 iterable 类型, 这里默认是数组类型
当所有 promsie 的状态变为 fulfilled 时, Promise.all 的状态也变为 fulfilled, 成员值为 promise 结果的一个数组

static all(promises) {
  return new Promise((resolve, reject) => {
    let ans = [], count = 0, len = promises.length
    // 将每个成功的 promsie 的结果记录下来
    const success = (result, idx) => {
      ans[idx] = result
      if (++ count === len) resolve(ans)
    }

    promises.forEach((p, idx) => { // 当 promise 实例成功时,调用 success 将其结果记录下来
      p.then(result => { success(result, idx) }, reject)
    })
  })
}

3.2.4 Promise.allSettled

接收一个 promise 的 iterable 类型的迭代对象, 这里默认为数组
无论 iterable 中的 promise 的结果为 fulfilled 还是 rejected, 该方法都会将其记录下来, 当所有 promise 的状态确定后, 返回一个数组, 其成员为记录着相应 promise 结果和状态的对象

static allSettled(promises) {
  return new Promise((resolve, reject) => {
    let ans = [], count = 0, len = promises.length
    // 根据不同的 promise 状态,生成不同的对象到数组中。当对象的数量足够时,确定 Promise.allSettled 返回的 promise 实例对象的状态
    const finish = (result, state, idx) => {
      ans[idx] = state === FULFILLED ?
        { status: state, value: result } :
        { status: state, reason: result }
      if (++ count === len) resolve(ans)
    }
    promises.forEach((p, idx) => {
      p.then(result => {
        finish(result, FULFILLED, idx)
      }, reason => {
        finish(reason, REJECTED, idx)
      })
    })
  })
}

3.2.5 Promise.race

返回的是第一个确定状态的 promise 实例对象的 result 和 state

static race(promises) {
  return new Promsie((resolve, reject) => {
    promises.forEach(p => {
      p.then(resolve, reject)
    })
  })
}

4. Promise 及其各方法

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

const resolvePromise = (promise2, x, resolve, reject) => {
  // 防止出现 let p = promiseObj.then(result => p, reason => p) 的情况
  if (promise2 === x) reject(new TypeError('cycling reference'))

  // 用于区分 onFulfilled/onRejected 返回的 x 是否是引用类型, 
  if (typeof x === 'object' && x != null || typeof x === 'function') {
    // 防止
    let called = false
    try {
      const then = x.then
      // 若 then 为函数, 则认为 x 为 Promise 的实例对象, 通过 then 来改变 promise2 的 result 和 state
      if (typeof then === 'function') {
        then.call(x, result => {
          if (called) return
          called = true
          resolvePromise(promise2, result, resolve, reject)
        }, reason => {
          if (called) return
          called = true
          reject(reason)
        })
      } else { // 若 then 不是函数, 则认为 x 并不是 Promise 实例对象, 将 x 作为 promise2 的 result 来保存
        resolve(x)
      }
    } catch(e) {
      if (called) return
      reject(e)
    }
  } else { // 若 x 不是引用类型, 则其必不是 Promise 实例对象, 则直接调用 resolve 方法, 令 promise2 的 result 为 x
    resolve(x)
  }
}

class Promise {
  constructor(executor) {
    this.state = PENDING
    this.result = undefined

    this.onFulfilledList = []
    this.onRejectedList = []

    const resolve = result => {
      if (this.state !== PENDING) return
      this.state = FULFILLED
      this.result = result

      this.onFulfilledList.forEach(fn => fn())
    }

    const reject = reason => {
      if (this.state !== PENDING) return
      this.state = REJECTED
      this.result = reason
      
      this.onRejectedList.forEach(fn => fn())
    }

    try {
      executor(resolve, reject)
    } catch(e) {
      reject(e)
    }
  }

  then(onFulfilled, onRejected) {
    if (typeof onFulfilled !== 'function') onFulfilled = result => result
    if (typeof onRejected !== 'function') onRejected = reason => { throw reason }
    
    const promise2 = new Promise((resolve, reject) => {
      if (this.state === PENDING) {
        this.onFulfilledList.push(() => {
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.result)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
        })

        this.onRejectedList.push(() => {
          queueMicrotask(() => {
            try {
              const x = onRejected(this.result)
              resolvePromise(promise2, x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          })
        })
      } else if (this.state === FULFILLED) {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.result)
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        })
      } else if (this.state === REJECTED) {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.result)
            resolvePromise(promise2, x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        })
      }
    })

    return promise2
  }

  catch(onRejected) {
    return this.then(_, onRejected)
  }

  finally(fn) {
    return this.then(fn, fn)
  }

  static resolve(result) {
    return new Promise((resolve, reject) => {
      resolve(result)
    })
  }

  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }

  static all(promises) {
    return new Promise((resolve, reject) => {
      let ans = [], count = 0, len = promises.length
      const success = (result, idx) => {
        ans[idx] = result
        if (++ count === len) resolve(ans)
      }
      promises.forEach((p, idx) => {
        p.then(result => { success(result, idx) }, reject)
      })
    })
  }

  static allSettled(promises) {
    return new Promise((resolve, reject) => {
      let ans = [], count = 0, len = promises.length
      const finish = (result, state, idx) => {
        ans[idx] = state === FULFILLED ? 
          { status: state, value: result } : 
          { status: state, reason: result }

        if (++ count === len) resolve(ans)
      }
      promises.forEach((p, idx) => {
        p.then(result => {
          finish(result, FULFILLED, idx)
        }, reason => {
          finish(reason, REJECTED, idx)
        })
      })
    })
  }

  static race(promises) {
    return new Promise((resolve, reject) => {
      promises.forEach(p => p.then(resolve, reject))
    })
  }
}

标签:resolve,const,实现,state,Promise,result,reject,模拟
From: https://www.cnblogs.com/suzukaze/p/Promise.html

相关文章