手写Promise

52 阅读2分钟

Promise基本使用

const p = new Promise((resolve, reject) => {
  resolve('ok')
  reject('error')
})
p.then(res => {
  console.log(res)
}, err => {
  console.log(err)
})
  1. Promise接收一个函数,这个函数会立即执行
  2. Promise状态不可逆
    1. pending
    2. fulfilled
    3. rejected
  3. 结果是resolve生效,忽略reject

基础实现

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

class myPromise{
  constructor(executor) {
    this.status = 'pending' // 保存当前处于哪一状态
    this.reason = undefined // 保存reject结果
    this.value = undefined  // 保存resolve结果

    const resolve = (value) => {
      console.log(value)
      this.value = value
    }

    const reject = (reason) => {
      console.log(reason)
      this.reason = reason
    }

    executor(resolve, reject)
  }
}

const p = new myPromise((resolve, reject) => {
  resolve('success')
  reject('fail')
  throw new Error('error')
})

问题:

  1. resolve和reject都执行了
  2. 最终status: rejected
  3. 无法捕获主体异常

添加状态不可逆

const resolve = (value) => {
  if(this.status === PENDING) {
    this.status = FULFILLED
    console.log(value)
    this.value = value
  }
}

const reject = (reason) => {
  if(this.status === PENDING) {
    this.status  = REJECTED
    console.log(reason)
    this.reason = reason
  }
}

捕获主体异常

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

实现then

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

// 测试
p.then(res => {
  console.log(res) // success
})

问题:

无法处理主体异步问题

const p = new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('timeout')
  }, 1000)
})
p.then(res => {
  console.log('res', res) // 这里不会执行
})

解决异步问题

因为执行then的时候status的状态还是pending,所以就没办法执行onFulfilled, 可以先将then的回调先保存起来,在resolve或reject时再调用

constructor(executor) {
  this.fulfilledCallbacks = [] // then 可以多次调用,使用数组保存
  this.rejectedCallbacks = []
}
then(onFulfilled, onRejected) {
  if(this.status === PENDING) {
    onFulfilled && this.fulfilledCallbacks.push(onFulfilled)
    onRejected && this.rejectedCallbacks.push(onRejected)
  }
}
const resolve = (value) => {
  this.fulfilledCallbacks.forEach(cb => {
    cb(this.value)
  })
}
const reject = (reason) => {
  this.fulfilledCallbacks.forEach(cb => {
    cb(this.reason)
  })
}

完整代码

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

class myPromise{
  constructor(executor) {
    this.status = 'pending' // 保存当前处于哪一状态
    this.reason = undefined // 保存reject结果
    this.value = undefined  // 保存resolve结果
    this.fulfilledCallbacks = []
    this.rejectedCallbacks = []

    const resolve = (value) => {
      if(this.status === PENDING) {
        this.status = FULFILLED
        // console.log(value)
        this.value = value

        this.fulfilledCallbacks.forEach(cb => {
          cb(this.value)
        })
      }
    }

    const reject = (reason) => {
      if(this.status === PENDING) {
        this.status  = REJECTED
        // console.log(reason)
        this.reason = reason

        this.rejectedCallbacks.forEach(cb => {
          cb(this.reason)
        })
      }
    }

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

  then(onFulfilled, onRejected) {
    if(this.status === FULFILLED) {
      onFulfilled(this.value)
    }
    if(this.status === REJECTED) {
      onFulfilled(this.reason)
    }
    if(this.status === PENDING) {
      onFulfilled && this.fulfilledCallbacks.push(onFulfilled)
      onRejected && this.rejectedCallbacks.push(onRejected)
    }
  }
}

const p = new myPromise((resolve, reject) => {
  // resolve('success')
  // reject('fail')
  // throw new Error('eeeeee')
  setTimeout(() => {
    resolve('timeout')
  }, 1000)
})
p.then(res => {
  console.log('res1==========================', res)
})
p.then(res => {
  console.log('res2==========================', res)
})

实现链式调用

其实就是返回一个新的promise实例

then(onFulfilled, onRejected) {
  const thenPromise = new myPromise((resolve, reject) => {
    if(this.status === FULFILLED) {
      try {
        const value = onFulfilled(this.value)
        resolve(value)
      } catch (error) {
        reject(error)
      }
    }
    if(this.status === REJECTED) {
      try {
        const value = onRejected(this.reason)
        resolve(value)
      } catch (error) {
        reject(error)
      }
    }
    if(this.status === PENDING) {
      onFulfilled && this.fulfilledCallbacks.push(() => {
        try {
          const value = onFulfilled(this.value)
          resolve(value)
        } catch (error) {
          reject(error)
        }
      })
      onRejected && this.rejectedCallbacks.push(() => {
        try {
          const value = onRejected(this.reason)
          resolve(value)
        } catch (error) {
          reject(value)
        }
      })
    }
  })

  return thenPromise
}

注意之前的onFulfilled && this.fulfilledCallbacks.push(onFulfilled)要改成下面这样

onFulfilled && this.fulfilledCallbacks.push(() => {
  try {
    const value = onFulfilled(this.value)
    resolve(value)
  } catch (error) {
    reject(error)
  }
})

因为之前是直接pushonFulfilled,无法拿到onFulfilled的结果

添加catch

catch可以看做是then只传了第二个参数

catch(onRejected) {
  this.then(undefined, onRejected)
}
  • 完整代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 统一处理异常
const catchFn = (fn, value, resolve, reject) => {
  try {
    const result = fn(value)
    resolve(result)
  } catch (error) {
    reject(error)
  }
}

class myPromise{
  constructor(executor) {
    this.status = 'pending' // 保存当前处于哪一状态
    this.value = undefined  // 保存resolve结果
    this.reason = undefined // 保存reject结果
    this.fulfilledCallbacks = []
    this.rejectedCallbacks = []

    const resolve = (value) => {
      if(this.status === PENDING) {
        this.status = FULFILLED
        // console.log(value)
        this.value = value

        while(this.fulfilledCallbacks.length) {
          this.fulfilledCallbacks.shift()(this.value)
        }
      }
    }

    const reject = (reason) => {
      if(this.status === PENDING) {
        this.status  = REJECTED
        // console.log(reason)
        this.reason = reason

        while(this.rejectedCallbacks.length) {
          this.rejectedCallbacks.shift()(this.reason)
        }
      }
    }

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

  then(onFulfilled, onRejected) {
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

    const thenPromise = new myPromise((resolve, reject) => {
      if(this.status === FULFILLED) {
        catchFn(onFulfilled, this.value, resolve, reject)
      }
      if(this.status === REJECTED) {
        catchFn(onRejected, this.reason, resolve, reject)
      }
      if(this.status === PENDING) {
        onFulfilled && this.fulfilledCallbacks.push(() => {
          catchFn(onFulfilled, this.value, resolve, reject)
        })
        onRejected && this.rejectedCallbacks.push(() => {
          catchFn(onRejected, this.reason, resolve, reject)
        })
      }
    })

    return thenPromise
  }

  catch(onRejected) {
    this.then(undefined, onRejected)
  }
}