美文网首页
经典前端面试题之如何用es5手写Promise

经典前端面试题之如何用es5手写Promise

作者: Promi5e | 来源:发表于2021-04-03 21:03 被阅读0次

前言

来新公司也有三周了,也逐渐适应了新氛围。这几天把面试时一个答得一塌糊涂的问题做了深入的研究:如何用es5手写Promise?今天做一下详细整理。

正文

分析Promise

Promise能够极大的方便我们管理异步操作。

  1. 因为es5没有class这个概念,所以Promise是一个构造函数,并且它在创建时立即执行,它接收一个函数作为参数,这个函数一般会有 两个参数resolve和reject,这两个参数其实是两个函数,它们都是使用者调用的,其中resolve在异步操作成功时调用,而reject会在异步操作失败时调用;
  2. Promise有三种状态pending(进行中),fulfilled(成功)和rejected(失败),Promise状态只能由pending变成fulfilled或者rejected,并且状态改变不可逆;
  3. then方法,接收两个回调函数作为参数,它们是Promise回调给使用者的,第一个为成功回调,第二个为失败回调,then方法返回一个Promise;
  4. catch方法,可以捕获到异步操作的失败回调,同时也可以捕获到then方法内部的异常,返回一个Promise;
  5. finally方法,无论异步操作结果如何都会执行的操作,返回一个Promise;
  6. resolve方法,会直接返回一个fulfilled状态的Promise;
  7. reject方法,会直接返回一个reject状态的Promise;
  8. all方法,接收一个数组作为参数,其中数组的每个成员必须是Promise对象,它返回一个Promise对象,只有数组中所有的Promise都成功时,会进入成功回调,否则会进入失败回调;
  9. race方法,同样接收一个包含Promise的数组作为参数,它会返回最先执行完的Promise的状态,执行成功就进入成功回调,失败就进入失败回调。

手写

有了以上对于Promise的分析和整理,我们就可以按部就班的开始手写自己的Promise
了。

  1. 构造函数
function MyPromise(fn) {
  this._cbs = []
  // 用于保存创建MyPromise对象时,resolve或者reject传进来的值,在then方法中再回传回去
  this._result = null 
  function resolve() {}
  function reject() {}
  // 创建MyPromise对象时,立即执行传进来的函数,并把内部的resolve和reject作为参数传递给fn
  fn(resolve, reject)
}
  1. 三个状态
function MyPromise(fn) {
  this.PENDING_STATE = 'pending'
  this.FULFILLED_STATE = 'fulfilled'
  this.REJECTED_STATE = 'rejected'
  this._state = this.PENDING_STATE
  this._cbs = []
  this._result = null
  function resolve() {}
  function reject() {}
  fn(resolve, reject)
}

引入状态以后,我们需要完善一下resolve和reject方法

function MyPromise(fn) {
  this.PENDING_STATE = 'pending'
  this.FULFILLED_STATE = 'fulfilled'
  this.REJECTED_STATE = 'rejected'
  this._state = this.PENDING_STATE
  this._cbs = []
  this._result = null
  var self = this
  // resolve函数只有pending状态才会执行,切换状态后,把传入的value保存,
  function resolve(v) {
    if (self._state !== self.PENDING_STATE) return
    self._result = v
    self._state = self.FULFILLED_STATE
    setTimeout(function() {
      self._cbs.forEach(function(cb) {
        cb.onFulfilled()
      })
    })
  }

  function reject(r) {
    if (self._state !== self.PENDING_STATE) return
    self._result = r
    self._state = self.REJECTED_STATE
    setTimeout(function() {
      self._cbs.forEach(function(cb) {
        cb.onRejected()
      })
    })
  }
  try {
    fn(resolve, reject)
  } catch(e) {
    reject(e)
  }
}
  1. then
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  if(typeof onFulfilled !== 'function') {
    onFulfilled = function() {}
  }
  if(typeof onRejected !== 'function') {
    onRejected = function() {}
  }
  var self = this
  return new MyPromise(function(resolve, reject) {
    function handle(cb) {
      try {
        var result = cb(self._result)
        // 链式调用
        if (result instanceof MyPromise) {
          result.then(function(v) {
            resolve(v)
          }, function(r) {
            reject(r)
          })
        } else {
          // 如果then内部的onFulfilled或者onRejected返回了一个值,则将这个值继续向下传递
          // 即Promise.resolve(1).then(() => 2).then(console.log) 此时打印结果为2
          if(self._state === self.FULFILLED_STATE) {
            resolve(result || self._result)
          } else {
            reject(result || self._result)
          }
        }
      } catch(e) {
        reject(e)
      }
    }
    // 异步操作没执行完,先保存回调函数,等待执行结果
    if (self._state === self.PENDING_STATE) {
      self._cbs.push({
        onFulfilled: function() {
          handle(onFulfilled)
        },
        onRejected: function() {
          handle(onRejected)
        }
      })
    }
    if (self._state === self.FULFILLED_STATE) {
      handle(onFulfilled)
    }
    if (self._state === self.REJECTED_STATE) {
      handle(onRejected)
    }
  })
}
  1. catch
MyPromise.prototype.catch = function(onRejected) {
  return this.then(undefined, onRejected)
}
  1. finally
MyPromise.prototype.finally = function(callback) {
  return this.then(callback, callback)
}
  1. resolve
MyPromise.resolve = function(value) {
  return new MyPromise(function(resolve, reject) {
    if(value instanceof MyPromise) {
      value.then(function(v) {
        resolve(v)
      }, function(r) {
        reject(r)
      })
    } else {
      resolve(value)
    }
  })
}
  1. reject
MyPromise.reject = function(r) {
  return new MyPromise(function(resolve, reject) {
    reject(r)
  })
}
  1. all
MyPromise.all = function(list) {
  var count = 0
  var results = []
  return new MyPromise(function(resolve, reject) {
    list.forEach(function(myPromise, index) {
      myPromise.then(function(v) {
        count++
        results[index] = v
        if(count === list.length) {
          resolve(results)
        }
      }, function(r) {
        reject(r)
      })
    })
  })
} 
  1. race
MyPromise.race = function(list) {
  return new MyPromise(function(resolve, reject) {
    list.forEach(function(myPromise) {
      myPromise.then(function(v) {
        resolve(v)
      }, function(r) {
        reject(r)
      })
    })
  })
}

总结

在整理这些东西之前,还以为自己对Promise掌握的还行,结果细究起来还有不少的盲点,做整理的过程会加深自己的理解和记忆,也方便日后查看。

相关文章

网友评论

      本文标题:经典前端面试题之如何用es5手写Promise

      本文链接:https://www.haomeiwen.com/subject/zphahltx.html