Promise实现

1、Promise是什么?

  Promise对象是ES6提供的用于表示一个异步操作的最终完成 (或失败)及其结果值。

2、Promise的作用?

     promise主要解决了ES5回调函数带来的回调地狱问题,在没有promise之前js在处理异步事件的时候主要采用的是回调函数方式,这种方式导致如果异步事件依赖较深会导致回调函数嵌套的层级很深。这样如果有bug那么查找起来就很麻烦,下面举一个简单例子:

function getUsersInfo() {
  setTimeout(() => {
     setTimeout(() => {
        setTimeout(() => {
          console.log("okokoko")
        }, 1000)
     }, 1000)
  }, 1000)
}
getUsersInfo()

  上面是我们假设的一个请求用户信息的方法,每一层的setTimeOut代表异步请求,如果下一层的数据请求需要上一层数据请求返回的值才可以进行。这样就出现了上面示例中的嵌套。上面只举例了三层,如果嵌套更深,代码逻辑更加不清晰这样不利于后期维护。如果有中间一个接口变了那个改起来的话就会很麻烦。

  好在ES6为我们提供了Promise,他有效的帮我们解决了回调地狱带来的麻烦

 

3、Promise介绍?

  详细的介绍大家可以看https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

  这里主要说:

    Promise提供了三种状态:

      1、pending 初始

      2、fulfilled 成功

      3、rejected  失败

    Promise三种状态转化方式是单向的 只能从 pending ------> fulfilled   或者  pending --------> rejected

    Promise构造函数接收一个 函数作为参数,该函数有两个参数也为函数 第一个函数代表成功执行,第二个函数代表失败执行,如下: 

const p = new Promise((resolve, reject) => {
      // 这里resolve 是成功执行的函数
      // 这里reject 是失败执行的函数
})

    Promise对象有两个属性 PromiseStatus(保存结果状态)  PromiseValue(保存结果值), 如下:

 

 

 

 4、Promise的简单实现

  根据上面介绍的Promise的特点我们可以实现如下代码: 

function Promise(excuter) {

  this.PromiseStatus = 'pending'  // 初始值为 pending
  this.PromiseValue = undefined   // 初始值为 undefined

  resolve = result => { // 该函数的作用是将 状态改为 resolved
    if (this.PromiseState !== 'pending') return // 状态是单向修改的 所以 只能修改 pending 状态
    this.PromiseStatus = 'resolved'
    this.PromiseValue = result
  }

  reject = error => {  // 该函数的作用是将 状态改为 rejected
    if (this.PromiseState !== 'pending') return // 状态是单向修改的 所以 只能修改 pending 状态
    this.PromiseStatus = 'rejected'
    this.PromiseValue = error
  }
  try { // 在Promise 中使用 throw 抛出异常的时候结果也会变为 rejected 所以这里需要做一下 try...catch捕获异常改变状态
    excuter(resolve, reject)
  } catch(e) {
    reject(e)
  }
}

 

  在测试的js文件中 执行如下代码:

const p = new Promise((resolve, reject) => {
      reject('123')
})
console.log(p)  // 会发现p中的状态已经被修改

  至此我们已经完成了第一步。

 

5、实现Promise的then方法

  then方法的参数是两个函数第一个是 Promise状态成功的回调,第二个参数是 Promise状态失败的回调,而且返回的还是一个 Promise对象,这个新Promise对象的状态根据 then方法执行的结果而定

    如果then方法的返回值是非Promsie则状态为 resolved

    如果then方法的返回值是Promise则状态根据返回的Promsie的状态而定

  根据上述介绍我们可以实现如下功能

Promise.prototype.then = function(onResolve, onReject) {
  return new Promise((resolve, reject) => {
    if (this.PromiseStatus === 'resolved') {
      try {
        const result = onResolve(this.PromiseValue) // 在成功状态下 才可以执行 第一个参数
        if (result instanceof Promise) {
          result.then(r => {  // 返回的Promise的状态由 then执行结果的状态而定
            resolve(r)
          }, error => {
            reject(error)
          })
        } else {
          resolve(result)
        }
      } catch(e) {
        reject(e)
      }
    }
    if (this.PromiseStatus === 'rejected') { // 在成功状态下 才可以执行 第二个参数
      try {
        const result = onReject(this.PromiseValue)
        if (result instanceof Promise) {
          result.then(r => { // 返回的Promise的状态由 then执行结果的状态而定
            resolve(r)
          }, error => {
            reject(error)
          })
        } else {
          resolve(result)
        }
      } catch(e) {
        reject(e)
      }
    }
  })
}

  上述代码可以看到有 冗余的部分那就是 处理 then函数返回值的时候,不论失败还是成功只是 执行的函数名不一样 所以可以进行代码合并如下:

Promise.prototype.then = function(onResolve, onReject) {
  return new Promise((resolve, reject) => {
    const callback = type => {
      try {
        const result = type(this.PromiseValue)
        if (result instanceof Promise) {
          result.then(r => {  // 返回的Promise的状态由 then执行结果的状态而定
            resolve(r)
          }, error => {
            reject(error)
          })
        } else {
          resolve(result)
        }
      } catch(e) {
        reject(e)
      }
    }
    if (this.PromiseStatus === 'resolved') {
      callback(onResolve)
    }
    if (this.PromiseStatus === 'rejected') {
      callback(onReject)
    }
  })
}

  根据上述代码我们可以实现Promise 中没有异步的情况,但是如果有异步怎么办,如果有异步情况 则在进行 执行then方法的时候状态还没有 改掉。如下代码

const p = new Promise((resolve, reject) => {
      setTimeout(() => { // 由于执行到这里发现需要等待则代码继续执行
        resolve('1234')
      }, 1000)
})
p.then(result => {
      console.log(result) // 执行到这里的时候 由于 还没有调用 resolve 或者 reject 则Promise的状态不会发生修改。所以这里实现then的时候需要加入对 pending 状态的实现
}, error => {
      console.warn(error)
})

  所以在 执行then方法的时候 PromiseStatus 的值还是为 'pending' 所以在then方法中我们还要处理 pending的情况,处理代码如下:下面代码需要加在 this.PromiseStatus === 'rejected' 判断的下面

if (this.PromiseStatus === 'pending') { // 如果是pending 状态则不能直接执行then里面的回调函数,需要将回调先保存起来然后等到调用 resolve或者reject的时候调用
      this.callbacks.push({ // 由于一个Promise可以有多个then处理 所以需要用数组进行存放
        onResolve: function() { // 将所有 成功需要执行的函数放在 onResolve 属性中
          callback(onResolve)
        },
        onReject: function() { // 将所有 失败需要执行的函数放在 onReject 属性中
          callback(onReject)
        }
      })
}

  上面的代码是对 Promise中存在异步事件的时候then函数中的处理方法,在then方法中将 绑定的函数 先进行保存起来,那什么时候执行呢, 当时是执行完 resolve 或者 reject之后执行, 所以需要对Promise构造函数中resolve, reject 函数进行改写 当然在Promise

函数需要添加 callbacks 属性用来存放 then方法中定义的回调, Promise构造函数中 resolve、reject函数改写如下:

resolve = result => { // 该函数的作用是将 状态改为 resolved
    if (this.PromiseState !== 'pending') return // 状态是单向修改的 所以 只能修改 pending 状态
    this.PromiseStatus = 'resolved'
    this.PromiseValue = result
    this.callbacks.forEach(fn => { // 遍历 then方法中保存的 方法
      fn.onResolve(this.PromiseValue) // resolve代表成功 所以执行保存的成功的回调函数
    })
  }

  reject = error => {  // 该函数的作用是将 状态改为 rejected
    if (this.PromiseState !== 'pending') return // 状态是单向修改的 所以 只能修改 pending 状态
    this.PromiseStatus = 'rejected'
    this.PromiseValue = error
    this.callbacks.forEach(fn => {  // 遍历 then方法中保存的 方法
      fn.onResolve(this.PromiseValue) // reject代表失败 所以执行保存的失败的回调函数
    })
  }

 

6、实现Promise对象的catch方法

  catch方法接收一个函数, 是Promise失败的处理函数,。返回值也是 Promsie。根据上面可以得到then的第一个参数是 Promise成功的回调,第二参数是Promise失败的回调,那么 可以借助 then去实现catch,代码如下

Promise.prototype.catch = function(onReject) {
  return this.then(null, onReject)  // 不传入成功的回调则只让处理 失败的回调 
}

 

7、优化then方法

  由于catch方法的实现借用了 then方法,导致then方法的第一参数为 null, 而且在正常使用过程中 then往往只被用来处理Promise成功的回调,所以第二个参数有可能也会为undefined: 所以then方法需要对传入的onResolve, onReject 方法进行判断处理,代码如下:

if (typeof onResolve !== 'function') { // 如果不是函数
    onResolve = value => value  // 由于成功的回调不需要处理所以直接返回 值就好
  }
  if (typeof onReject !== 'function') {
    onReject = reason => {  // 由于是失败的回调, 所以默认函数抛出错误, 
      throw reason
    }
  }

8、由于 then或者catch的方法都会在 顺序执行之后才执行 如下:

 

 

   所以我们需要对 代码中的所有的callback()函数 添加 setTimeOut 使其在下一个EventLoop中执行

9、至此已经简单的实现了 Promise构造函数、then、catch方式。 完成的代码如下:

  Promise构造函数:

function Promise(excuter) {

  this.PromiseStatus = 'pending'  // 初始值为 pending
  this.PromiseValue = undefined   // 初始值为 undefined
  this.callbacks = []

  resolve = result => { // 该函数的作用是将 状态改为 resolved
    this.PromiseValue = result
    if (this.PromiseStatus !== 'pending') return // 状态是单向修改的 所以 只能修改 pending 状态
    this.PromiseStatus = 'resolved'
    setTimeout(() => {
      this.callbacks.forEach(fn => { // 遍历 then方法中保存的 方法
        fn.onResolve(this.PromiseValue) // resolve代表成功 所以执行保存的成功的回调函数
      })
    })
  }

  reject = error => {  // 该函数的作用是将 状态改为 rejected
    if (this.PromiseStatus !== 'pending') return // 状态是单向修改的 所以 只能修改 pending 状态
    this.PromiseStatus = 'rejected'
    this.PromiseValue = error
    setTimeout(() => {
      this.callbacks.forEach(fn => {  // 遍历 then方法中保存的 方法
        fn.onResolve(this.PromiseValue) // reject代表失败 所以执行保存的失败的回调函数
      })
    })
  }
  try { // 在Promise 中使用 throw 抛出异常的时候结果也会变为 rejected 所以这里需要做一下 try...catch捕获异常改变状态
    excuter(resolve, reject)
  } catch(e) {
    reject(e)
  }
}

  then方法:

Promise.prototype.then = function(onResolve, onReject) {
  return new Promise((resolve, reject) => {
    const callback = type => {
      try {
        const result = type(this.PromiseValue)
        if (result instanceof Promise) {
          result.then(r => {  // 返回的Promise的状态由 then执行结果的状态而定
            resolve(r)
          }, error => {
            reject(error)
          })
        } else {
          resolve(result)
        }
      } catch(e) {
        reject(e)
      }
    }
    if (this.PromiseStatus === 'resolved') {
      setTimeout(() => {
        callback(onResolve)
      })
    }
    if (this.PromiseStatus === 'rejected') {
      setTimeout(() => {
        callback(onReject)
      })
    }
    if (this.PromiseStatus === 'pending') { // 如果是pending 状态则不能直接执行then里面的回调函数,需要将回调先保存起来然后等到调用 resolve或者reject的时候调用
      this.callbacks.push({ // 由于一个Promise可以有多个then处理 所以需要用数组进行存放
        onResolve: function() { // 将所有 成功需要执行的函数放在 onResolve 属性中
          callback(onResolve)
        },
        onReject: function() { // 将所有 失败需要执行的函数放在 onReject 属性中
          callback(onReject)
        }
      })
    }
  })
}

  catch方法:

Promise.prototype.catch = function(onReject) {
  return this.then(null, onReject) // 不传入成功的回调则只让处理 失败的回调 
}

10、Promise还提供了针对多个 Promise处理的方法 例如

  all:返回一个Promise,如果所有的Promise都成功了 则返回的值是 每个Promise成功值 构成的数组 并且顺序是 Promise的顺序,如果有一个失败的Promise则在 失败的Promise的时候直接返回,并且Promise的结果值为 失败Promise的结果值

  race:返回一个Promise, 返回所有Promise中第一个成功或者失败的Promise 的结果值

  根据如上功能可以实现如下代码:

   all:

  

Promise.all = function(promises) {
  let arr = []
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) { // 遍历传入的Promise数组
      try {
        promises.then(r => {
          arr[i] = r    // 保存每一个成功Promise 的值
          if (arr.length === promises.length) {  // 要全部成功了才返回 成功状态
            resolve(arr)
          }
        }, error => {
          reject(error)  // 只要有一个Promise 失败直接返回 失败
        })
      } catch (e) {
        reject(e)
      }
    }
  })
}

  race:

Promise.race = function(promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) { // 遍历传入的Promise数组
      try {
        promises.then(r => { // 有一个成功了 立马返回 成功
          resolve(arr)
        }, error => {
          reject(error)  // 有一个失败直接返回 失败
        })
      } catch (e) {
        reject(e)
      }
    }
  })
}

 

该文只是简单的实现了Promise的基本功能,如有问题请指出 谢谢....

posted @ 2021-06-23 18:05  Web涛涛  阅读(381)  评论(0编辑  收藏  举报