手把手教你实现一个promise

前言

网上看了很多关于promise的源码讲解,都是上来直接讲的源码实现,本文将带你通过分析promise的使用方法来了解promise的具体实现


先看一段代码

const p = new Promise((resolve, reject) => {
  resolve('resolve')
  setTimeout(() => {
    reject('reject')
  })
})

p.then(res => {
  console.log('ok: ' + res)
}, err => {
  console.log('err: ' + err)
})

// ok: resolve
上面代码运行结果为ok: resolve,得知一旦调用了resolve之后reject则无法调用,反之也是一样的

得出结论:
1 promise是一个类
2 内部有三个状态,状态一旦发生改变则无法再变为其他状态
3 实例化这个类需要传递两个方法,分别是:resolve和reject 并且这两个参数也是方法,resolve的作用就是
4 实例方法then方法接受两个回调函数函数作为参数分别是成功的和失败的回调函数,当调用resolve时会触发then的成功回调函数,当调用reject时会触发then的失败回调函数。

// 上边说到内部有三种状态 假定三种状态为: pendding fulfilled rejected
// pendding:最初的状态
// fulfilled: 当调用resolve方法后由pendding =》fulfilled
// rejected: 当调用reject方法后由pendding =》rejected
// 且状态一旦发生改变后就无法再进行改变

// 三种状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise{
	// 接受一个立即执行的方法
	constructor(fn) {
		fn(this._resolve, this._rejected)
	}
	// 状态 初始为 pendding
	_status = PENDING
	// 该方法将状态改为fulfilled
	_resolve = () => {
		// 判断当前状态是否为 pending 是: 改变状态为 FULFILLED 否:不做任何操作
	    if (this._status !== PENDING) return
	    this._status = FULFILLED
	}
	// 该方法将状态改为rejected
	_rejected = () => {
		// 判断当前状态是否为 pending 是: 改变状态为 REJECTED 否:不做任何操作
	    if (this._status !== PENDING) return
	    this._status = REJECTED
	}
}

进入主题

then

通过第一段代码得知then是实例方法且接受两个回调函数函数作为参数分别是成功的和失败的回调函数,当调用resolve时会触发then的成功回调函数,当调用reject时会触发then的失败回调函数。

class MyPromise{
	...
	...
	// 保存调用resolve 和 rejected时的参数 在调用then方法时需要将对应信息作为参数回传
	_resolveInfo = undefined
	_rejectedInfo = undefined
	// 保存then方法中的两个回调函数 在异步处理完成后(状态改变后)调用相应函数 then方法可被同一实例多次调用所以为数组
	 _resolveCallBack = []
	 _rejectedCallBack = []
	 
	// 该方法将状态改为fulfilled
	_resolve = () => {
		// 判断当前状态是否为 pending 是: 改变状态为 FULFILLED 否:不做任何操作
	    if (this._status !== PENDING) return
	    this._status = FULFILLED
	    // 保存成功信息
	    this._resolveInfo = res
	    // 调用成功回调函数并将失败信息作为参数 _resolveCallBack
	    while (this._resolveCallBack.length) this._resolveCallBack.shift()(this._resolveInfo)
	}
	// 该方法将状态改为rejected
	_rejected = () => {
		// 判断当前状态是否为 pending 是: 改变状态为 REJECTED 否:不做任何操作
	    if (this._status !== PENDING) return
	    this._status = REJECTED
	    // 保存失败信息
	    this._rejectedInfo = err
	    // 调用失败回调函数并将失败信息作为参数 _rejectedCallBack
	    while (this._rejectedCallBack.length) this._rejectedCallBack.shift()(this._rejectedInfo)
	}
	// then 两个参数 分别为 成功的回调和失败的回调
	then(fulfilledCallBack, rejectedCallBack) {
	    // 如果状态为 fulfilled 调用成功回调并传递成功信息
	    if (this._status === FULFILLED) {
	      fulfilledCallBack(this._resolveInfo)
	    // 如果状态为 rejected 调用失败回调并传递失败信息
	    } else if (this._status === REJECTED) {
	      rejectedCallBack(this._rejectedInfo)
	    // 如果状态为 pending 则保存回调 待状态改变后触发相应回调 (异步)
	    } else {
	      // 这里是异步情况 将 回调函数保存到callback数组中 并在状态改变时调用(_resolve,_rejected 函数中调用)
	      this._resolveCallBack.push(fulfilledCallBack)
	      this._rejectedCallBack.push(rejectedCallBack)
	    }
	}
}

then的链式调用

接下来我们来实现then的链式调用 我们再来看一个例子
const res = new Promise((resolve, reject) => {
  resolve('成功1')
})

const res1 = new Promise((resolve, reject) => {
  resolve('成功2')
})

res.then(res => {
  // 这里我们返回一个普通类型变量
  return 'a'
}).then(res => {
  // 链式调用这里的res是上一个then的返回如果不返回则为undefined
  console.log(res)
})

res.then(res => {
  // 这里我们将一个promise对象返回过去
  return res1
}).then(res => {
  // 这里的res接受的是上一个then返回的promise对象的计算结果
  console.log(res)
})

res.then(undefined, err => {
  // 这里根本没有走到 因为这是失败的回调 而创建promise实例的时候内部调用的是成功的方法 所以这个失败函数中的代码是无效的
  console.log(err)
  return 'b'
}).then(res => {
  // 链式传递 因为上一个then方法的成功回调并不存在, 参数会作为这次函数的形参
  console.log(res, 1)
})

res.then(res => {
  // 当前成功回调中抛出一个错误 这个错误会在下一个then函数的失败回调中捕获
  throw new Error('err1')
}).then(undefined, err => {
  console.log(err.message, 2)
})


输出结果为:
// a
// 成功1 1
// err1 2
// 成功2
通过上边的案例我们得出结论: 1 then 方法可以链式调用 2 then 方法中会返回一个Promise实例 且 回调方法中的返回值或者抛出的失败会作为下一个then方法中相应回调的形参 3 then 回调方法中可以返回普通类型和Promise实例
class MyPromise {
  ...
  ...
  // then 两个参数 分别为 成功的回调和失败的回调
  then(fulfilledCallBack, rejectedCallBack) {
    // 判断是存在回调函数
    fulfilledCallBack = fulfilledCallBack ? fulfilledCallBack : val => val
    rejectedCallBack = rejectedCallBack ? rejectedCallBack : err => err
    let promise1 = new MyPromise((resolve, reject) => {
        // 如果状态为 fulfilled 调用成功回调并传递成功信息
        // queueMicrotask 会创建一个异步任务 这里用异步任务的原因是 MyPromise 还没有实例化完成 所以得不到 promise1 这里为什么要用 queueMicrotask 而不是 setTimeout 因为promise是微任务 setTimeout是宏任务 queueMicrotask会创建一个微任务
        if (this._status === FULFILLED) {
          queueMicrotask(() => {
            try {
              const res = fulfilledCallBack(this._resolveInfo) 
              this._isPromise(promise1, res, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        // 如果状态为 rejected 调用失败回调并传递失败信息
        } else if (this._status === REJECTED) {
          queueMicrotask(() => {
            try {
              const res = rejectedCallBack(this._rejectedInfo)
              this._isPromise(promise1, res, resolve, reject)
            } catch (error) {
              reject(error)         
            }
          })
        // 如果状态为 pending 则保存回调 待状态改变后触发相应回调 (异步)
        } else {
          this._resolveCallBack.push((res) => {
            queueMicrotask(() => {
              try {
                const e = fulfilledCallBack(res)
                this._isPromise(promise1, e, resolve, reject)
              } catch (error) {
                reject(error)
              }
            })
          })
          this._rejectedCallBack.push((err) => {
            queueMicrotask(() => {
              try {
                const e = rejectedCallBack(err)
                this._isPromise(promise1, e, resolve, reject)
              } catch (error) {
                console.log(error)
              }
            })
          })
        }
    })
    return promise1
  }
  // 判断传递过来的是 promise 还是 普通类型  如果为promise类型需要判断是否和当前实例是否相等
  _isPromise = (promise1, res, resolve, reject) => {
    if (promise1 === res) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    if (res instanceof MyPromise) {
      res.then(resolve, reject)
    } else {
      resolve(res)
    }
  }
}
至此为止我们就实现了then方法 下边我们实现catch方法

catch

先来看一段事例代码
const res = new Promise((resolve, reject) => {
  reject('err')
})

// catch方法可以链式调用也可以直接使用 它会捕获整条链上的错误
res.then((res => {})).then().catch(err => {
  console.log(err)
})

打印结果为:
// err
得出结论: catch方法和then方法非常像,就是没有成功回调的then方法
class MyPromise {
  ...
  ...
  // catch
  catch = (callback) => {
    return this.then(undefined, callback)
  }
}
最后附上完整版代码
// 三种状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 接受一个立即执行的方法
  constructor(fn) {
    try {
      fn(this._resolve, this._rejected)
    } catch (error) {
      this._rejected(error)
    }
  }

  // catch
  catch = (callback) => {
    // 链式调用需要return
    return this.then(undefined, callback)
  }

  // 状态 初始为 pendding
  _status = PENDING

  // 保存调用resolve 和 rejected时的参数 在调用then方法时需要将对应信息作为参数回传
  _resolveInfo = undefined
  _rejectedInfo = undefined

  // 保存then方法中的两个回调函数 在异步处理完成后(状态改变后)调用相应函数 then方法可被同一实例多次调用 为数组
  _resolveCallBack = []
  _rejectedCallBack = []

  // 定义两个改变状态的方法 需要使用箭头函数 不然会改变this的指向
  _resolve = res => {
    // 判断当前状态是否为 pending 是: 改变状态为 FULFILLED 否:不做任何操作
    if (this._status !== PENDING) return
    this._status = FULFILLED
    // 保存成功信息
    this._resolveInfo = res
    // 调用回调
    while (this._resolveCallBack.length) this._resolveCallBack.shift()(this._resolveInfo)
  }
  _rejected = err => {
    // 判断当前状态是否为 pending 是: 改变状态为 REJECTED 否:不做任何操作
    if (this._status !== PENDING) return
    this._status = REJECTED
    // 保存失败信息
    this._rejectedInfo = err
    // 调用回调
    while (this._rejectedCallBack.length) this._rejectedCallBack.shift()(this._rejectedInfo)
  }

  // then 两个参数 分别为 成功的回调和失败的回调
  then(fulfilledCallBack, rejectedCallBack) {
    // 判断是存在回调函数
    fulfilledCallBack = fulfilledCallBack ? fulfilledCallBack : val => val
    rejectedCallBack = rejectedCallBack ? rejectedCallBack : err => {throw err}
    let promise1 = new MyPromise((resolve, reject) => {
      // 如果状态为 fulfilled 调用成功回调并传递成功信息
      if (this._status === FULFILLED) {
        queueMicrotask(() => {
          try {
            const res = fulfilledCallBack(this._resolveInfo) 
            this._isPromise(promise1, res, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      // 如果状态为 rejected 调用失败回调并传递失败信息
      } else if (this._status === REJECTED) {
        queueMicrotask(() => {
          try {
            const res = rejectedCallBack(this._rejectedInfo)
            this._isPromise(promise1, res, resolve, reject)
          } catch (error) {
            reject(error)         
          }
        })
      // 如果状态为 pending 则保存回调 待状态改变后触发相应回调 (异步)
      } else {
        this._resolveCallBack.push((res) => {
          queueMicrotask(() => {
            try {
              const e = fulfilledCallBack(res)
              this._isPromise(promise1, e, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this._rejectedCallBack.push((err) => {
          queueMicrotask(() => {
            try {
              const e = rejectedCallBack(err)
              this._isPromise(promise1, e, resolve, reject)
            } catch (error) {
              console.log(error)
            }
          })
        })
      }
    })
    return promise1
  }

  // 判断传递过来的是 promise 还是 普通类型  如果为promise类型需要判断是否和当前实例是否相等
  _isPromise = (promise1, res, resolve, reject) => {
    if (promise1 === res) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    if (res instanceof MyPromise) {
      res.then(resolve, reject)
    } else {
      resolve(res)
    }
  }
}

到此本文中promise功能已经实现了,基本上可以直接使用了,当然promise还有静态方法,比如:Promise.all Promise.resolve 等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值