资讯专栏INFORMATION COLUMN

万字详解JavaScript手写一个Promise

3403771864 / 215人阅读

  在面试中,Promise会被经常问,下面为大家整合关于Promise各种问题。

  Promise核心原理实现

  用Promise来写代码

  Promise的使用分析

  Promise是一个在执行的时候,需要传递一个执行器(回调函数)进去,执行器会立即执行的一一个类。

  Promise中的状态分为三个,分别是:

  pending→等待

  fulfilled→成功

  rejected→失败

  状态的切换只有两种,分别是:

  pending→fulfilled

  pending→rejected

  须知:当状态发生改变时就不会再次改变:

  执行器中的两个参数,分别是resolve和reject,这两个回调函数,调用resolve是从pending状态到fulfilled,调用reject是从状态pending到rejected。传递给这两个回调函数的参数会作为成功或失败的值。

  Promise的实例对象具有一个then方法,该方法接受两个回调函数,分别来处理成功与失败的状态,then方法内部会进行判断,然后根据当前状态确定调用的回调函数。then方法应该是被定义在原型对象中的。

  then的回调函数有一个值,当达到这个值时候,表示成功后返回的值;当返回不是这值时,就表示失败。

  MyPromise的实现

  根据我们上面的分析,写出如下代码:

  MyPromise.js
  // 定义所有状态的常量
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  // Promise实质上就是一个类,首先创建一个Promise的类
  class MyPromise {
  // 实例化Promise时需要一个回调函数,该回调函数立即执行
  constructor(executor) {
  // 在调用executor需要传递两个回调函数,分别是resolve和reject
  executor(this.resolve, this.reject)
  }
  // Promise 的状态
  status = PENDING
  // 记录成功与失败的值
  value = undefined
  reason = undefined
  resolve = (value) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
  // 形参value表示,调用resolve时传递的参数
  // 如果当前状态不是pending,就直接跳出该逻辑
  if (this.status !== PENDING) return
  // 将状态修改为成功
  this.status = FULFILLED
  // 将传入的值进行保存
  this.value = value
  }
  reject = (reason) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
  // 形参reason表示,调用reject时传递的失败的原因
  // 如果当前状态不是pending,就直接跳出该逻辑
  if (this.status !== PENDING) return
  // 将状态修改为失败
  this.status = REJECTED
  // 保存失败的原因
  this.reason = reason
  }
  // then方法的实现
  then (onFulfilled, onRejected) {
  // 判断当前状态,根据状态调用指定回调
  if (this.status === FULFILLED) {
  // 将成功的值作为参数返回
  onFulfilled(this.value)
  } else if (this.status === REJECTED) {
  // 将失败的原因作为参数返回
  onRejected(this.reason)
  }
  }
  }
  // 导出Promise
  module.exports = MyPromise

  现在我们就来写一段代码验证一下上面的代码

  验证resolve:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })
  /* 输出
  成功
  */

  验证reject:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  reject('失败')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })
  /* 输出
  失败
  */

  验证状态不可变:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
  reject('失败')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })
  /* 输出
  成功
  */

  在Promise中加入异步操作

  当代码中存在异步操作时,表示Promise将毫无用处,

  例如下面这段代码:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  setTimeout(resolve, 2000, '成功')
  })
  promise.then(value => {
  console.log(value);
  }, reason => {
  console.log(reason);
  })

  这段代码2000ms后没有任何输出,为了解决这个问题我们将对自己编写的类进行如下操作:

  创建两个实例方法用于存储我们传入的成功与失败的处理逻辑。

  为then方法添加状态为pending时的处理逻辑,这时将传递进来的属性保存到实例上。

  在成功或者失败时调用相应的传递进来的回调函数(实例属性存在函数的情况下)。

  实现代码如下:

  // MyPromise.js
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  class MyPromise {
  constructor(executor) {
  executor(this.resolve, this.reject)
  }
  status = PENDING
  value = undefined
  reason = undefined
  // 存储成功与失败的处理逻辑
  onFulfilled = undefined
  onRejected = undefined
  resolve = (value) => {
  if (this.status !== PENDING) return
  this.status = FULFILLED
  this.value = value
  // 如果将状态修复为成功,调用成功的回调
  this.onFulfilled && this.onFulfilled(this.value)
  }
  reject = (reason) => {
  if (this.status !== PENDING) return
  this.status = REJECTED
  this.reason = reason
  // 如果将状态修复为失败,调用失败的回调
  this.onRejected && this.onRejected(this.reason)
  }
  then (onFulfilled, onRejected) {
  if (this.status === FULFILLED) {
  onFulfilled(this.value)
  } else if (this.status === REJECTED) {
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
  this.onFulfilled = onFulfilled
  this.onRejected = onRejected
  }
  }
  }
  module.exports = MyPromise

  这里的this.onFulfilled && this.onFulfilled(this.value)表示如果该属性存在就调用这个方法。

  现在我们重新执行一开始上面那一段代码,2s后会成功输出成功。

  实现then方法的多次调用

  Promise实例中存在要给then方法,允许我们在Promise实例中链式调用,每个then方法还会返回一个Promise实例,

  如下图所示:

1.png

  Promise实例方法then是可以多次进行调用,而我们现在自己封装的却执行调用一次,现在根据新需要来重新改写我们的代码,

  实现思路如下:

  定义可以存储多个回调的数组,用于存储多个回调函数。

  如果是同步执行的代码,执行后立即知道执行结果,所以可以直接调用回调函数。

  如果是异步代码,需要将每次回调函数保存到数组中,然后状态变化时依次调用函数。

  实现代码如下:

  // MyPromise.js
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  class MyPromise {
  constructor(executor) {
  executor(this.resolve, this.reject)
  }
  status = PENDING
  value = undefined
  reason = undefined
  // 存储成功与失败的处理逻辑
  onFulfilled = []
  onRejected = []
  resolve = (value) => {
  if (this.status !== PENDING) return
  this.status = FULFILLED
  this.value = value
  // 如果将状态修复为成功,调用成功的回调
  while (this.onFulfilled.length) {
  // Array.prototype.shift() 用于删除数组第一个元素,并返回
  this.onFulfilled.shift()(this.value)
  }
  }
  reject = (reason) => {
  if (this.status !== PENDING) return
  this.status = REJECTED
  this.reason = reason
  // 如果将状态修复为失败,调用失败的回调
  while (this.onRejected.length) {
  // Array.prototype.shift() 用于删除数组第一个元素,并返回
  this.onRejected.shift()(this.reason)
  }
  }
  then (onFulfilled, onRejected) {
  if (this.status === FULFILLED) {
  onFulfilled(this.value)
  } else if (this.status === REJECTED) {
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  }
  }
  module.exports = MyPromise

  这里我们通过数组的shift()方法,该方法删除数组的第一个元素,并返回,返回的这个值正好是一个回调函数,然后调用该函数即可实现该功能。

  实现then的链式调用

  想要实现then的链式调用,主要解决两个问题:

  返回的是一个新的MyPormise实例。

  then的返回值作为下一次的链式调用的参数。

  这里分为两种情况:

  直接返回一个值,可以直接作为值使用

  返回一个新的MyPormise实例,此时就需要判断其状态

  实现代码如下:

  // MyPromise.js
  /* 省略的代码同上 */
  class MyPromise {
  /* 省略的代码同上 */
  // then方法的实现
  then (onFulfilled, onRejected) {
  // then 方法返回一个MyPromise实例
  return new MyPromise((resolve, reject) => {
  // 判断当前状态,根据状态调用指定回调
  if (this.status === FULFILLED) {
  // 将成功的值作为参数返回
  // 保存执行回调函数的结果
  const result = onFulfilled(this.value)
  // 如果返回的是一个普通的值,直接调用resolve
  // 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
  resolvePromise(result, resolve, reject)
  } else if (this.status === REJECTED) {
  // 将失败的原因作为参数返回
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  })
  }
  }
  function resolvePromise (result, resolve, reject) {
  // 判断传递的result是不是MyPromise的实例对象
  if (result instanceof MyPromise) {
  // 说明是MyPromise的实例对象
  // 调用.then方法,然后在回调函数中获取到具体的值,然后调用具体的回调
  // result.then(value => resolve(value), reason => reject(reason))
  // 简写
  result.then(resolve, reject)
  } else {
  resolve(result)
  }
  }
  module.exports = MyPromise

  then方法链式调用识别Promise对象自返回

  在Promise中,如果then方法返回的是自己的promise对象,则会发生promise的嵌套,这个时候程序会报错,

  测试代码如下:

  var promise = new Promise((resolve, reject) => {
  resolve(100)
  })
  var p1 = promise.then(value => {
  console.log(value)
  return p1
  })
  // 100
  // Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

  想要解决这个问题其实我们只需要在then方法返回的MyPromise实例对象与then中回调函数返回的值进行比对,如果相同的返回一个reject的MyPromise实例对象,并创建一个TypeError类型的Error。

  实现代码如下:

  // MyPromise.js
  /* 省略的代码同上 */
  then (onFulfilled, onRejected) {
  // then 方法返回一个MyPromise实例
  const promise = new MyPromise((resolve, reject) => {
  // 判断当前状态,根据状态调用指定回调
  if (this.status === FULFILLED) {
  // 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
  // 如果不变成异步的话是在函数内获取不到promise的
  setTimeout(() => {
  // 将成功的值作为参数返回
  // 保存执行回调函数的结果
  const result = onFulfilled(this.value)
  // 如果返回的是一个普通的值,直接调用resolve
  // 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
  resolvePromise(promise, result, resolve, reject)
  }, 0)
  } else if (this.status === REJECTED) {
  // 将失败的原因作为参数返回
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  })
  return promise
  }
  }
  function resolvePromise (promise, result, resolve, reject) {
  // 这里修改一下该函数,如果return的Promise实例对象,也就是传入的promise===result的话,说明在promise中return的是当前promise对象。
  if (promise === result) {
  // 这里调用reject,并抛出一个Error
  // return 是必须的,阻止程序向下执行
  return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // 判断传递的result是不是MyPromise的实例对象
  if (result instanceof MyPromise) {
  // 说明是MyPromise的实例对象
  // 调用.then方法,然后在回调函数中获取到具体的值,然后调用具体的回调
  // result.then(value => resolve(value), reason => reject(reason))
  // 简写
  result.then(resolve, reject)
  } else {
  resolve(result)
  }
  }
  module.exports = MyPromise

  这里then方法中的setTimeout的作用并不是延迟执行,而是为了调用resolvePromise函数时,保证创建的promise存在。

  捕获错误及 then 链式调用其他状态代码补充

  Promise异常想在还未处理,为了保证代码的稳定性,需要做处理。

  捕获执行器错误

  现在我们需要对执行器进行异常捕获,如果发生异常,就将我们的状态修改为rejected。

  捕获执行器的错误也比较简单,只需要在构造函数中加入try...catch语句就可以,

  实现代码如下:

  constructor(executor) {
  try {
  // 在调用executor需要传递两个回调函数,分别是resolve和reject
  executor(this.resolve, this.reject)
  } catch (e) {
  // 发生异常调用reject
  this.reject(e)
  }
  }

  测试代码如下:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  throw new Error('执行器错误')
  })
  promise.then(value => {
  console.log(value);
  }, error => {
  console.log(error.message);
  })
  /* 输出
  执行器错误
  */

  捕获then中的报错

  现在我们需要对then中的异常捕获到,并在下一次链式调用中传递到then的第二个函数中,实现的方式也是通过try...catch语句,

  示例代码如下:

  // then方法的实现
  then (onFulfilled, onRejected) {
  // then 方法返回一个MyPromise实例
  const promise = new MyPromise((resolve, reject) => {
  // 判断当前状态,根据状态调用指定回调
  if (this.status === FULFILLED) {
  // 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
  // 如果不变成异步的话是在函数内获取不到promise的
  setTimeout(() => {
  try {
  // 将成功的值作为参数返回
  // 保存执行回调函数的结果
  const result = onFulfilled(this.value)
  // 如果返回的是一个普通的值,直接调用resolve
  // 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else if (this.status === REJECTED) {
  // 将失败的原因作为参数返回
  onRejected(this.reason)
  } else {
  // 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
  this.onFulfilled.push(onFulfilled)
  this.onRejected.push(onRejected)
  }
  })
  return promise
  }

  测试代码如下:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
  })
  // 第一个then方法中的错误要在第二个then方法中捕获到
  promise.then(value => {
  console.log('resolve', value)
  throw new Error('then的执行过程中遇到异常')
  }).then(null, reason => {
  console.log(reason.message)
  })
  /* 输出
  resolve 成功
  then的执行过程中遇到异常
  */

  错误与异步状态的链式调用

  现在只对成功状态的then进行的链式调用以及错误处理,错误与异步状态未进行处理,其实处理起来也是一样的,

  示例代码如下:

  // then方法的实现
  then (onFulfilled, onRejected) {
  // then 方法返回一个MyPromise实例
  const promise = new MyPromise((resolve, reject) => {
  // 判断当前状态,根据状态调用指定回调
  if (this.status === FULFILLED) {
  // 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
  // 如果不变成异步的话是在函数内获取不到promise的
  setTimeout(() => {
  try {
  // 将成功的值作为参数返回
  // 保存执行回调函数的结果
  const result = onFulfilled(this.value)
  // 如果返回的是一个普通的值,直接调用resolve
  // 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else if (this.status === REJECTED) {
  // 失败的处理同成功处理,只是调用的回调函数不同
  setTimeout(() => {
  try {
  const result = onRejected(this.reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else {
  this.onFulfilled.push((value) => {
  setTimeout(() => {
  try {
  const result = onFulfilled(value)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  this.onRejected.push((reason) => {
  setTimeout(() => {
  try {
  const result = onRejected(reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  }
  })
  return promise
  }

  测试代码如下:

  const MyPromise = require('./myPromise')
  let promise = new MyPromise((resolve, reject) => {
  setTimeout(resolve, 2000, '成功')
  })
  // 第一个then方法中的错误要在第二个then方法中捕获到
  promise.then(value => {
  console.log('resolve', value)
  throw new Error('then的执行过程中遇到异常')
  }).then(null, reason => {
  console.log(reason.message)
  })
  /* 输出
  resolve 成功
  then的执行过程中遇到异常
  */

  将then方法的参数变成可选参数

  Promise中的then方法其实是两个可以可选参数,如果我们不传递任何参数的话,里面的结果是向下传递的,直到捕获为止,

  例如下面这段代码:

  new Promise((resolve, reject) => {
  resolve(100)
  })
  .then()
  .then()
  .then()
  .then(value => console.log(value))
  // 最后一个then输入100

  这段代码可以理解为:

  new Promise((resolve, reject) => {
  resolve(100)
  })
  .then(value => value)
  .then(value => value)
  .then(value => value)
  .then(value => console.log(value))

  所以说我们只需要在没有传递回调函数时,赋值一个默认的回调函数即可。

  实现代码如下:

  // then方法的实现
  then (onFulfilled, onRejected) {
  // 如果传递函数,就是用传递的函数,否则指定一个默认值,用于参数传递
  onFulfilled = onFulfilled ? onFulfilled : value => value
  // 同理
  onRejected = onRejected ? onRejected : reason => { throw reason }
  // then 方法返回一个MyPromise实例
  const promise = new MyPromise((resolve, reject) => {
  // 判断当前状态,根据状态调用指定回调
  if (this.status === FULFILLED) {...
  } else if (this.status === REJECTED) {...
  } else {...
  }
  })
  return promise
  }

  Promise.all方法的实现

  关于all()方法的使用,可以参数Promise.all()。简单的说Promise.all()会将多个Promise实例包装为一个Promise实例,且顺序与调用顺序一致,

  示例代码如下:

  function p1 () {
  return new Promise((resolve, reject) => {
  setTimeout(() => {
  resolve('p1')
  }, 2000)
  })
  }
  function p2 () {
  return new Promise((resolve, reject) => {
  setTimeout(() => {
  resolve('p2')
  }, 0)
  })
  }
  Promise.all(['a', 'b', p1(), p2(), 'c']).then(result => {
  console.log(result)
  // ["a", "b", "p1", "p2", "c"]
  })

  在这段代码中,我们的p1的执行是延迟了2s的,这里如果不使用Promise.all()的话最终顺序是与我们调用不同的。

  现在我们来分析一下all()的实现思路:

  all()方法可以通过类直接调用,所以是一个静态方法

  all()方法接收一个数组,数组中的值可以是一个普通值,也可以是一个MyPromise的实例对象

  return一个新的MyPromise实例对象

  遍历数组中的每一个值,判断值得类型,如果是一个普通值得话直接将值存入一个数组;如果是一个MyPromise的实例对象的话,会调用then方法,然后根据执行后的状态,如果失败的话调用新的MyPromise实例对象中的rejecte,如果是成功话将这个值存入一个数组

  存入数组时计数,如果存入的数量达到传入的数组长度,说明调用完毕,执行resolve并将最终的结果数组作为参数返回。

  实现代码:

  /**
  * @description: 将多个Promise实例合并为一个Promise实例
  * @param {*} array Promise或者普通值
  * @returns {Promise}
  */
  static all (array) {
  // 用于存放最终结果的数组
  let result = []
  // 用于计算当前已经执行完的实例的数量
  let count = 0
  // 最后返回的是一个Promise实例
  return new MyPromise((resolve, reject) => {
  /**
  * @description: 将执行完毕的值加入结果数组,并根据实际情况决定是否调用resolve
  * @param {*} result 存放结果的数组
  * @param {*} index 要加入的索引
  * @param {*} value 要加入数组的值
  * @param {*} resolve Promise中的resolve
  */
  function addResult (result, index, value, resolve) {
  // 根据索引值,将结果堆入数组中
  result[index] = value
  // 执行完毕一个 count+1,如果当前值等于总长度的话说明已经执行结束了,可以直接调用resolve,说明已经成功执行完毕了
  if (++count === array.length) {
  // 将执行结果返回
  resolve(result)
  }
  }
  // 遍历穿入的数组,每个都执行then方法,获取到最终的结果
  array.forEach((p, index) => {
  // 判断p是不是MyPromise的实例,如果是的话调用then方法,不是直接将值加入数组中
  if (p instanceof MyPromise) {
  p.then(
  // 成功时将结果直接加入数组中
  value => {
  addResult(result, index, value, resolve)
  },
  // 如果失败直接返回失败原因
  reason => {
  reject(reason)
  }
  )
  }
  else {
  addResult(result, index, p, resolve)
  }
  })
  })
  }
  Promise.resolve方法的实现

  关于Promise.resolve()方法的用法可以参考Promise.resolve()与Promise.reject()。

  我们实现的思路主要如下:

  该方法是一个静态方法

  该方法接受的如果是一个值就将该值包装为一个MyPromise的实例对象返回,如果是一个MyPromise的实例对象,调用then方法返回。

  实现代码如下:

  static resolve (value) {
  // 如果是MyPromise的实例,就直接返回这个实例
  if (value instanceof MyPromise) return value
  // 如果不是的话创建一个MyPromise实例,并返回传递的值
  return new MyPromise((resolve) => {
  resolve(value)
  })
  }

  finally方法的实现

  关于finally方法可参考finally(),实现该方法的实现代码如下:

  finally (callback) {
  // 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
  // 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
  // 我们需要在回调之后拿到成功的回调,所以需要把value也return
  // 失败的回调也抛出原因
  // 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
  return this.then(value => {
  // 把callback调用之后返回的promise传递过去,并且执行promise,且在成功之后返回value
  return MyPromise.resolve(callback()).then(() => value)
  }, reason => {
  // 失败之后调用的then方法,然后把失败的原因返回出去。
  return MyPromise.resolve(callback()).then(() => { throw reason })
  })
  }

  测试:

  function p1 () {
  return new MyPromise((resolve, reject) => {
  setTimeout(() => {
  resolve('p1')
  }, 2000)
  })
  }
  function p2 () {
  return new MyPromise((resolve, reject) => {
  reject('p2 reject')
  })
  }
  p2().finally(
  () => {
  console.log('finally p2')
  return p1()
  }
  ).then(
  value => {
  console.log(value)
  }, reason => {
  console.log(reason)
  }
  )
  // finally p2
  // 两秒之后执行p2 reject

  catch方法的实现

  关于catch方法可以参考catch(),实现该方法其实非常简单,只需要在内部调用then方法,不传递第一个回调函数即可,

  实现代码如下:

  catch (callback) {
  return this.then(null, failCallback)
  }

  测试如下:

  function p () {
  return new MyPromise((resolve, reject) => {
  reject(new Error('reject'))
  })
  }
  p()
  .then(value => {
  console.log(value)
  })
  .catch(reason => console.log(reason))

  完整代码

  // MyPromise.js
  // 定义所有状态的常量
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  // Promise实质上就是一个类,首先创建一个Promise的类
  class MyPromise {
  // 实例化Promise时需要一个回调函数,该回调函数立即执行
  constructor(executor) {
  try {
  // 在调用executor需要传递两个回调函数,分别是resolve和reject
  executor(this.resolve, this.reject)
  } catch (e) {
  // 发生异常调用reject
  this.reject(e)
  }
  }
  // Promise 的状态
  status = PENDING
  // 记录成功与失败的值
  value = undefined
  reason = undefined
  // 存储成功与失败的处理逻辑
  onFulfilled = []
  onRejected = []
  resolve = (value) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
  // 形参value表示,调用resolve时传递的参数
  // 如果当前状态不是pending,就直接跳出该逻辑
  if (this.status !== PENDING) return
  // 将状态修改为成功
  this.status = FULFILLED
  // 将传入的值进行保存
  this.value = value
  // 如果将状态修复为成功,调用成功的回调
  // this.onFulfilled && this.onFulfilled(this.value)
  while (this.onFulfilled.length) {
  // Array.prototype.shift() 用于删除数组第一个元素,并返回
  this.onFulfilled.shift()(this.value)
  }
  }
  reject = (reason) => {// 这里使用箭头函数是为了使其内部的this指向为该实例化后的对象
  // 形参reason表示,调用reject时传递的失败的原因
  // 如果当前状态不是pending,就直接跳出该逻辑
  if (this.status !== PENDING) return
  // 将状态修改为失败
  this.status = REJECTED
  // 保存失败的原因
  this.reason = reason
  // 如果将状态修复为失败,调用失败的回调
  // this.onRejected && this.onRejected(this.reason)
  while (this.onRejected.length) {
  // Array.prototype.shift() 用于删除数组第一个元素,并返回
  this.onRejected.shift()(this.reason)
  }
  }
  // then方法的实现
  then (onFulfilled, onRejected) {
  // 如果传递函数,就是用传递的函数,否则指定一个默认值,用于参数传递
  onFulfilled = onFulfilled ? onFulfilled : value => value
  // 同理
  onRejected = onRejected ? onRejected : reason => { throw reason }
  // then 方法返回一个MyPromise实例
  const promise = new MyPromise((resolve, reject) => {
  // 判断当前状态,根据状态调用指定回调
  if (this.status === FULFILLED) {
  // 这里并不需要延迟执行,而是通过setTimeout将其变成异步函数
  // 如果不变成异步的话是在函数内获取不到promise的
  setTimeout(() => {
  try {
  // 将成功的值作为参数返回
  // 保存执行回调函数的结果
  const result = onFulfilled(this.value)
  // 如果返回的是一个普通的值,直接调用resolve
  // 如果是一个MyPromise实例,根据返回的解决来决定是调用resolve,还是reject
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else if (this.status === REJECTED) {
  // 失败的处理同成功处理,只是调用的回调函数不同
  setTimeout(() => {
  try {
  const result = onRejected(this.reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  } else {
  // 表示既不是成功,也不是失败。这个时候保存传递进来的两个回调
  // this.onFulfilled.push(onFulfilled)
  // this.onRejected.push(onRejected)
  this.onFulfilled.push((value) => {
  setTimeout(() => {
  try {
  const result = onFulfilled(value)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  this.onRejected.push((reason) => {
  setTimeout(() => {
  try {
  const result = onRejected(reason)
  resolvePromise(promise, result, resolve, reject)
  } catch (error) {
  reject(error)
  }
  }, 0)
  })
  }
  })
  return promise
  }
  catch (callback) {
  return this.then(null, callback)
  }
  finally (callback) {
  // 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
  // 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
  // 我们需要在回调之后拿到成功的回调,所以需要把value也return
  // 失败的回调也抛出原因
  // 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
  return this.then(value => {
  // 把callback调用之后返回的promise传递过去,并且执行promise,且在成功之后返回value
  return MyPromise.resolve(callback()).then(() => value)
  }, reason => {
  // 失败之后调用的then方法,然后把失败的原因返回出去。
  return MyPromise.resolve(callback()).then(() => { throw reason })
  })
  }
  /**
  * @description: 将多个Promise实例合并为一个Promise实例
  * @param {*} array Promise或者普通值
  * @returns {Promise}
  */
  static all (array) {
  // 用于存放最终结果的数组
  let result = []
  // 用于计算当前已经执行完的实例的数量
  let count = 0
  // 最后返回的是一个Promise实例
  return new MyPromise((resolve, reject) => {
  /**
  * @description: 将执行完毕的值加入结果数组,并根据实际情况决定是否调用resolve
  * @param {*} result 存放结果的数组
  * @param {*} index 要加入的索引
  * @param {*} value 要加入数组的值
  * @param {*} resolve Promise中的resolve
  */
  function addResult (result, index, value, resolve) {
  // 根据索引值,将结果堆入数组中
  result[index] = value
  // 执行完毕一个 count+1,如果当前值等于总长度的话说明已经执行结束了,可以直接调用resolve,说明已经成功执行完毕了
  if (++count === array.length) {
  // 将执行结果返回
  resolve(result)
  }
  }
  // 遍历穿入的数组,每个都执行then方法,获取到最终的结果
  array.forEach((p, index) => {
  // 判断p是不是MyPromise的实例,如果是的话调用then方法,不是直接将值加入数组中
  if (p instanceof MyPromise) {
  p.then(
  // 成功时将结果直接加入数组中
  value => {
  addResult(result, index, value, resolve)
  },
  // 如果失败直接返回失败原因
  reason => {
  reject(reason)
  }
  )
  }
  else {
  addResult(result, index, p, resolve)
  }
  })
  })
  }
  static resolve (value) {
  // 如果是MyPromise的实例,就直接返回这个实例
  if (value instanceof MyPromise) return value
  // 如果不是的话创建一个MyPromise实例,并返回传递的值
  return new MyPromise((resolve) => {
  resolve(value)
  })
  }
  }
  function resolvePromise (promise, result, resolve, reject) {
  // 这里修改一下该函数,如果return的Promise实例对象,也就是传入的promise===result的话,说明在promise中return的是当前promise对象。
  if (promise === result) {
  // 这里调用reject,并抛出一个Error
  // return 是必须的,阻止程序向下执行
  return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // 判断传递的result是不是MyPromise的实例对象
  if (result instanceof MyPromise) {
  // 说明是MyPromise的实例对象
  // 调用.then方法,然后在回调函数中获取到具体的值,然后调用具体的回调
  // result.then(value => resolve(value), reason => reject(reason))
  // 简写
  result.then(resolve, reject)
  } else {
  resolve(result)
  }
  }
  module.exports = MyPromise

      上面内容很多,大家可以慢慢消化。


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/127765.html

相关文章

  • 马蹄疾 | 详解 JavaScript 异步机制及发展历程(万字长文)

    摘要:本文从入手,系统的回顾的异步机制及发展历程。需要提醒的是,文本没有讨论的异步机制。本文是专题系列文章之一,后续会有更多专题推出地址持续更新博客地址文章排版真的很漂亮如果觉得对你有帮助,欢迎来点或者来我的博客亲口告诉我本文从Event Loop、Promise、Generator、async await入手,系统的回顾 JavaScript 的异步机制及发展历程。 需要提醒的是,文本没有讨论 ...

    KoreyLee 评论0 收藏0
  • 马蹄疾 | 详解 JavaScript 异步机制及发展历程(万字长文)

    摘要:本文从入手,系统的回顾的异步机制及发展历程。需要提醒的是,文本没有讨论的异步机制。这就是之前提到的事件触发线程。其实无论是请求还是定时器还是事件,我们都可以统称它们为事件。第二阶段,引擎线程专注于处理事件。将外元素的事件回调放入调用栈。 functionshowImg(url){ varframeid=frameimg+Math.random(); window.img=window....

    shaonbean 评论0 收藏0
  • 22道JavaScript高频手写面试题

    JavaScript笔试部分 点击关注本公众号获取文档最新更新,并可以领取配套于本指南的 《前端面试手册》 以及最标准的简历模板. 实现防抖函数(debounce) 防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。 那么与节流函数的区别直接看这个动画实现即可。 showImg(https://segmentfault.com/img/remote/146000002...

    Alan 评论0 收藏0
  • 2019-我的前端面试题

    摘要:先说下我面试情况,我一共面试了家公司。篇在我面试的众多公司里,只有同城的面问到相关问题,其他公司压根没问。我自己回答的是自己开发组件面临的问题。完全不用担心对方到时候打电话核对的问题。 2019的5月9号,离发工资还有1天的时候,我的领导亲切把我叫到办公室跟我说:阿郭,我们公司要倒闭了,钱是没有的啦,为了不耽误你,你赶紧出去找工作吧。听到这话,我虎躯一震,这已经是第2个月没工资了。 公...

    iKcamp 评论0 收藏0
  • 前端进阶资源整理

    摘要:前端进阶进阶构建项目一配置最佳实践状态管理之痛点分析与改良开发中所谓状态浅析从时间旅行的乌托邦,看状态管理的设计误区使用更好地处理数据爱彼迎房源详情页中的性能优化从零开始,在中构建时间旅行式调试用轻松管理复杂状态如何把业务逻辑这个故事讲好和 前端进阶 webpack webpack进阶构建项目(一) Webpack 4 配置最佳实践 react Redux状态管理之痛点、分析与...

    BlackMass 评论0 收藏0

发表评论

0条评论

3403771864

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<