首先,Promise 中有三种状态:pending(等待态)、fulfilled(完成态)、rejected(拒绝态),并且这只能从 pending 到 fulfilled 或者从 pending 到 rejected,并且此过程不可逆。
我们先来实现 Promise 的基本结构:
当我们 new 一个 Promise 时,会传入一个执行函数:new Promise((resolve, reject) => {})
这个执行函数里有两个参数,resolve、reject 方法都是在 Promise 类里定义的,外部调用这个方法时,还可以往里面传入参数,Promise 拿到这个值后保存起来,以供 .then 调用
.then 方法需要指定成功的回调和失败的回调,根据 state 状态的不同来执行对应的回调
class Promise{
  constructor(executor){
    this.state = "pending"  // 初始为 pending 态
    this.value = ''
    this.reson = ''
    const resolve = (value) => {
      this.value = value  // 将传入的 value 保存起来,以便 then 调用
      if(this.state === 'pending'){ // 只能从 pending 变到 fulfilled
        this.state = 'fulfilled'  // resolve 后,将状态置为 fulfilled
      }
    }
    const reject = (reson) => {
      this.reson = reson
      if(this.state === 'pending'){
        this.state = 'rejected'
      }
    }
    executor(resolve, reject)
  }
  then(onfulfilled, onrejected){
    if(this.state === 'fulfilled'){
      onfulfilled(this.value) // 将之前 resolve 的值传入
    }else if(this.state === 'rejected'){
      onrejected(this.reson)  // 将之前 reject 的值传入
    }
  }
}
以上,我们实现了两个功能:
- 实现了 Promise 基本结构和状态切换
- 实现基本的 then 方法,根据状态判断执行的回调
但是,上面的 Promise 还不能支持异步代码。
因为当 then 执行时,只对 fulfilled、rejected 两种状态做了判断,如果代码是异步的话,当执行到 then 方法时,resolve、reject 方法还没执行,还是 pending 态,所以我们这里需要对 pending 态做处理:
暂时将回调保存起来,等到 resolve、reject 执行的时候,才去执行对应的回调
class Promise{
  constructor(executor){
    this.state = "pending"
    this.value = ''
    this.reson = ''
    this.onfulfilledCallbacks = []  // 存放 .then 中成功的回调
    this.onrejectedCallbacks = [] // 存放 .then 中失败的回调
    const resolve = (value) => {
      this.value = value
      if(this.state === 'pending'){
        this.state = 'fulfilled'
      }
      this.onfulfilledCallbacks.forEach(fn => fn(this.value)) // 当 resolve 执行时,执行 then 中指定的成功回调
    }
    const reject = (reson) => {
      this.reson = reson
      if(this.state === 'pending'){
        this.state = 'rejected'
        this.onrejectedCallbacks.forEach(fn => fn(this.reson))
      }
    }
    executor(resolve, reject)
  }
  then(onfulfilled, onrejected){
    if(this.state === 'fulfilled'){
      onfulfilled(this.value)
    }else if(this.state === 'rejected'){
      onrejected(this.reson)
    }else if(this.state === 'pending'){ // 当 state 还未变化时,先将成功和失败的回调存起来
      this.onfulfilledCallbacks.push(onfulfilled)
      this.onrejectedCallbacks.push(onrejected)
    }
  }
}
以上,我们已经实现了:
- 实现了 Promise 基本结构和状态切换
- 实现基本的 then 方法,根据状态判断执行的回调
- 支持 Promise 异步
但是目前我们的 .then 只能调用一次,还不能实现链式调用 .then,要实现也很简单,只需要让 then 方法返回一个 promise 实例即可。
但是注意:这里返回的 promise 实例必须是一个全新的 promise,这样才能保证后续 then 中的状态可以改变,不然的话从 new 完 Promise 之后状态就一直保持成一个了。。。
同时,then 的回调执行的返回值还会传给下一个 then,所以还要把返回值 resolve 出去
then(onfulfilled, onrejected){
  const promise2 = new Promise((resolve, reject) => {
    if(this.state === 'fulfilled'){
      const x = onfulfilled(this.value)
      resolve(x)
    }else if(this.state === 'rejected'){
      const r = onrejected(this.reson)
      resolve(r)
    }else if(this.state === 'pending'){
      this.onfulfilledCallbacks.push((value) => {
        const x = onfulfilled(value)
        resolve(x)  // 将回调执行的返回值 resolve 出去,resolve 的值会挂载在返回的新的 promise 实例上的 value 属性上,也就是调用了这个实例里的 resolve 方法。下一个 then(也就是返回的这个新 promise 实例的 then) 可以拿到这个值
      })
      this.onrejectedCallbacks.push((reson) => {
        const r = onrejected(reson)
        resolve(r)  // 注意:即使是失败的回调,回调执行完成后,下一个状态依然是 fulfilled,除非出错!(后面我们将处理这个问题)
      })
    }
  })
  return promise2 // 返回一个全新的 promise 实例
}
以上,我们已经实现了:
- 实现了 Promise 基本结构和状态切换
- 实现基本的 then 方法,根据状态判断执行的回调
- 支持 Promise 异步
- 每个 then 返回一个新 promise 实例,同时将回调执行的返回值 resolve出去
我们现在来考虑一个问题,上面 then 中将回调执行的返回值 resolve 直接出去了,如果返回值是一个普通的值的话,这样没有问题。
但是,假如是个 promise,那就不能这样直接 resolve 了
如果返回值是个 promise 实例,我们就必须调用这个 promise 的 then,让这个 promise 执行,拿到这个 promise resolve 的值
我们需要写一个方法来处理返回值
then(onfulfilled, onrejected){
  const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {  // 这里加定时器,为了让里面的代码异步,保证传入 promise2 的时候, promise2 已经初始化完了,这也就解释了为什么 Promise 的 then 方法是异步的 
      if(this.state === 'fulfilled'){
        const x = onfulfilled(this.value)
        resolvePromise(promise2, x, resolve, reject)
      }
      
      else if(this.state === 'rejected'){
        const r = onrejected(this.reson)
        resolvePromise(promise2, r, resolve, reject)
      }
      
      else if(this.state === 'pending'){
        this.onfulfilledCallbacks.push((value) => {
          const x = onfulfilled(value)
          resolvePromise(promise2, x, resolve, reject) // 用特定的方法处理返回值
        })
        this.onrejectedCallbacks.push((reson) => {
          const r = onrejected(reson)
          resolvePromise(promise2, r, resolve, reject)
        })
      }
    }, 0)
  })
  return promise2
}
function resolvePromise(promise2, x, resolve, reject) {
  // 判断 x,如果是一般值直接 resolve,如果是一个 promise,要等待 promise resolve 或 reject
  if (promise2 === x) {
    return new TypeError('循环引用!')
  }
  else if (x instanceof Promise) {  // x 是 promise 实例
    x.then(y => {
      resolvePromise(promise2, y, resolve, reject)  // 此时的 y 可能还是一个 promise,所以需要递归调用 resolvePromise,直到解析出一个普通值,就将这个值通过 "传进来的 promise2 的 resolve 方法" 传出去
    }, r => {
      reject(r) // x 内部 reject 了或出错了
    })
  }
  
  else { // 普通值,直接 resolve
    resolve(x)
  }
}
这段代码需要注意的地方比较多:
- 为了保证 promise2可以拿到,加了一个定时器使代码异步
- 对 then 中所有 resolve 的值都需要采用 resolvePromise方法,因为回调函数的返回值可能是 promise 实例
- 在 resolvePromise方法中,需要校验是否循环引用,同时需要注意 promise resolve 的值还是 promise 的情况
以上,我们实现的功能有:
- 实现了 Promise 基本结构和状态切换
- 实现基本的 then 方法,根据状态判断执行的回调
- 支持 Promise 异步
- 每个 then 返回一个新 promise 实例,对回调执行的返回值进行判断,如果是普通值就直接 resolve出去,如果是一个 promise 实例,就执行 then 方法,直到得到一个普通值。同时,我们让 then 方法变成异步的了
接下来要考虑的就是,当我们第一次 new Promise 的时候,如果 resolve 的值也是一个 promise,也需要等待这个 promise 执行完 then,如下:
new Promise((resolve, reject) => {
  resolve(Promise.resolve(123))
})
.then(d => {
  console.log(d)  // 123
})
所以我们需要对 resolve 方法进行完善
const resolve = (value) => {
  if(value instanceof Promise){// 第一次 new Promise 时,resolve 的值如果是一个 promise
    // 调用 then,传入 resolve,resolve 中又进行是否为 promise 的判断调用,递归调用直到 value 不是一个 promise
    return value.then(resolve, reject)
  }
  if (this.state === 'pending') { // 只有 pedding 态可修改,并且不可逆
    this.state = 'fulfilled'
    this.value = value
    this.onfulfilledCallbacks.forEach(fn => fn(value)) // 执行 resolve 回调
  }
}
以上,我们实现的功能有:
- 实现了 Promise 基本结构和状态切换
- 实现基本的 then 方法,根据状态判断执行的回调
- 支持 Promise 异步
- 每个 then 返回一个新 promise 实例,对回调执行的返回值进行处理。同时,让 then 方法变成异步
- 对第一次 new Promise时 resolve 的值进行 Promise / 非Promise 的处理
回顾一下我们上面的代码,好像一直没有出现 reject 的情况,在 Promise 中,出现 reject 的情况有几种:
- 发生错误
- 主动 reject
我们对代码中需要进行错误捕获的地方做一下处理,顺便把 catch 方法实现了
class Promise {
  constructor(executor) {
    this.state = "pending"
    this.value = ''
    this.reson = ''
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = [] 
    const resolve = (value) => {
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value 
        this.onfulfilledCallbacks.forEach(fn => fn(value))
      }
    }
    const reject = (reson) => {
      this.reson = reson
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.onrejectedCallbacks.forEach(fn => fn(this.reson))
      }
    }
    try { // 错误捕获
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  then(onfulfilled, onrejected) {
    const promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        if (this.state === 'fulfilled') {
          try { // 错误捕获
            const x = onfulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }
        else if (this.state === 'rejected') {
          try { // 错误捕获
            const r = onrejected(this.reson)
            resolvePromise(promise2, r, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }
        else if (this.state === 'pending') {
          this.onfulfilledCallbacks.push((value) => {
            try { // 错误捕获
              const x = onfulfilled(value) 
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }) 
          this.onrejectedCallbacks.push((reson) => {
            try { // 错误捕获
              const r = onrejected(reson) 
              resolvePromise(promise2, r, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        }
      },0)
    })
    return promise2
  }
  catch(rejectFn){  // 错误处理函数
    return this.then(null, rejectFn)  // catch 其实就是第一个参数为 null 的 then 方法
  }
}
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return new TypeError('循环引用!')
  }
  else if (x instanceof Promise) {
    x.then(y => {
      resolvePromise(promise2, y, resolve, reject)
    },
    r => {
      reject(r)
    })
  }
  else {
    resolve(x)
  }
}
我们已经基本完善我们的 Promise 了,不过还需要考虑一种极端情况:
假如我中间传了一个空的 then,new Promise 中 resolve 的值仍然可以被传递下去,被下一个 then 拿到并打印。
new Promise((resolve, reject) => {
  resolve(123)
})
.then()
.then(v => {
  console.log(v)
})
这就需要给 then 传一个默认的函数参数,不传的话默认将拿到的值 return 出去
很简单,一行搞定
then(onfulfilled = v=>v, onrejected = r=>r){
  // ...
}
以上,我们的 Promise 就实现得差不多了,回顾一下我们实现的思路:
- 实现了 Promise 基本结构和状态切换
- 实现基本的 then 方法,根据状态判断执行的回调
- 支持 Promise 异步
- 每个 then 返回一个新 promise 实例,对回调执行的返回值进行 Promise / 非Promise 的处理。同时,让 then 方法变成异步
- 对第一次 new Promise时 resolve 的值进行 Promise / 非Promise 的处理
- 给 Promise 内部添加错误处理和 catch 方法
- 给 then 设置默认参数,不传的时候默认把值传递下去
还剩下一些 Promise 类的静态方法,这里我们也一并实现了:
Promise.resolve = function(value){
  //  Promise.resolve 实际上就是创建一个新的 Promise 实例返回,同时将传入的 value 参数 resolve 出去
  return new Promise((resolve, reject) => {
    resolve(value)
  })
}
Promise.reject = function(reson){
  return new Promise((resolve, reject) => { // Promise.reject 原理同上
    reject(reson)
  })
}
这里着重讲一下 Promise.all 和 Promise.race 方法
- Promise.all:
 Promise.all接收一个任务数组,数组元素 (可能是promise或其他值) 并发执行,如果是Promise就执行他,拿到resolve的值,如果是其他普通值就直接存起来,执行完成后的值存放在一个结果数组中
 Promise.all执行后会返回一个 新的Promise实例,并且会将结果数组resolve出去,下一个then中可以拿到
很明显,我们只需要在内部实现一个计数器,每个任务元素完成后将计数器加 1,只要达到了任务数组的 length 长度即可
- Promise.race
 这个方法就是:比比谁最快执行完
 遍历数组参数,执行每一个元素 (同样的,注意区分 Promise 和 非Promise)
 对于 Promise 实例,Promise 执行完成后,直接 resolve
 对于普通值,直接 resolve
Promise.all = function (p) {
  return new Promise((resolve, reject) => {
    let count = 0
    const result = []  // 结果数组
    function processData(index, value) {  // 存放每个 任务执行完后的值,并计数,计数完成后将 result 数组 resolve 出去
      result[index] = value
      if (++count === p.length) {
        resolve(result)
      }
    }
    p.forEach((cur, index) => {
      if (cur instanceof Promise) { // promise 实例
          cur.then(v => {
            processData(index, v)
          }, r => {
            reject(r) // 只要任何一个 promise 出错,就 reject
          })
      } else { // 普通值
        processData(index, cur)
      }
    })
  })
}
Promise.race = function (p) {
  return new Promise((resolve, reject) => { // 只要一个完成了,直接 resolve,resolve 出来的值就是最快执行完的
    p.forEach(cur => {
      if(cur instanceof Promise){
        cur.then(r => { // 执行 promise 然后 resolve 
          resolve(r)
        })
      }else { // 不是 promise,直接 resolve
        resolve(cur)
      }
    })
  })
}
以上就完成了对 Promise 的编写,完整代码如下 (无注释版):
class Promise {
  constructor(executor) {
    this.state = "pending"
    this.value = ''
    this.reson = ''
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = [] 
    const resolve = (value) => {
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value 
        this.onfulfilledCallbacks.forEach(fn => fn(value))
      }
    }
    const reject = (reson) => {
      this.reson = reson
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.onrejectedCallbacks.forEach(fn => fn(this.reson))
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  then(onfulfilled = v=>v, onrejected = r=>r) {
    const promise2 = new Promise((resolve, reject) =>{
      setTimeout(() =>{
        if (this.state === 'fulfilled') {
          try {
            const x = onfulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }
        else if (this.state === 'rejected') {
          try {
            const r = onrejected(this.reson)
            resolvePromise(promise2, r, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }
        else if (this.state === 'pending') {
          this.onfulfilledCallbacks.push((value) =>{
            try {
              const x = onfulfilled(value) 
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }) 
          this.onrejectedCallbacks.push((reson) =>{
            try {
              const r = onrejected(reson) 
              resolvePromise(promise2, r, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        }
      },0)
    })
    return promise2
  }
  catch(rejectFn){
    return this.then(null, rejectFn)
  }
}
function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return new TypeError('循环引用!')
  }
  else if (x instanceof Promise) {
    x.then(y => {
      resolvePromise(promise2, y, resolve, reject)
    },
    r => {
      reject(r)
    })
  }
  else {
    resolve(x)
  }
}
Promise.resolve = function(value){
  return new Promise((resolve, reject) => {
    resolve(value)
  })
}
Promise.reject = function(reson){
  return new Promise((resolve, reject) => {
    reject(reson)
  })
}
Promise.all = function (p) {
  return new Promise((resolve, reject) => {
    let count = 0
    const result = []
    function processData(index, value) {
      result[index] = value
      if (++count === p.length) {
        resolve(result)
      }
    }
    p.forEach((cur, index) => {
      if (cur instanceof Promise) {
          cur.then(v => {
            processData(index, v)
          }, r => {
            reject(r)
          })
      } else {
        processData(index, cur)
      }
    })
  })
}
Promise.race = function (p) {
  return new Promise((resolve, reject) => {
    p.forEach(cur => {
      if(cur instanceof Promise){
        cur.then(r => {
          resolve(r)
        })
      }else { 
        resolve(cur)
      }
    })
  })
}
感谢你看到这里 (▽)
欢迎关注更多个人博客










