Promise: 手写Promise

前言

ES6里的Promise是个原理性蛮复杂但是使用起来很简单的异步解决方案。我们有必要系统、深入的学习Promise的使用规则,并能根据规则手动实现一个完整的Promise,下面我们就根据官方的规则一步一步自定义Promise。
我们边写代码,边测试。Promise.js为源码,index.html为测试代码。

一、初始化结构搭建

1.1 index.html文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>我是Promise</title>
    <script src="./Promise.js"></script>
  </head>
  <body>
    <script>
      let p = new Promise((resolve, reject) => {
      
      
        resolve('guoyu')
      })
      console.log(p)
      p.then(value => {
      
      
        console.log(value)
      }, reason => {
      
      
        console.log(reason)
      })
    </script>
  </body>
</html>

1.2 Promise.js文件

function Promise(executor) {
    
    }

Promise.prototype.then = function(onResolved, onRejected) {
    
    }

二、构造函数中resolve与reject

在new Promise的时候,构造函数的参数executor是个函数,而且在Promise规则中是同步调用的。

function Promise(executor) {
    
    
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    }

  // 声明reject函数
  function reject(data) {
    
    }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  executor(resolve, reject)
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    }

2.1 resolve/reject函数的实现

在Promise规则中,resolve/reject有两个作用:

  • 改变对象状态 PromiseState
  • 设置对象结果值 PromiseResult
    在这里插入图片描述
    我们可以在构造函数中设置两个属性:PromiseState、PromiseResult,并赋予初始值,PromiseState初始值是 ‘pending’,因为状态值只可能有两种变化
  • pending --> fulfilled
  • pending --> rejected
this.PromiseState = 'pending'
this.PromiseResult = null
function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 这里的this指向全局window
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
  }

  // 声明reject函数
  function reject(data) {
    
    
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  executor(resolve, reject)
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    }

在这里插入图片描述

三、抛出异常改变状态

改变状态有三种情况

  1. resolve:pending —> fulfilled
  2. reject: pending —> rejected
  3. throw XXX 抛出异常: pendg —> rejected
    前面讲了1和2,现在对3抛出异常进行处理
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>我是Promise</title>
    <script src="./Promise.js"></script>
  </head>
  <body>
    <script>
      let p = new Promise((resolve, reject) => {
      
      
        // resolve('ok')
        // reject('error')
        // 抛出异常
        throw 'throwError'
      })
      console.log(p)
    </script>
  </body>
</html>

Promise.js中没有处理,所以直接报错了
在这里插入图片描述
如何处理呢?
try … catch 才能处理throw的异常,那么try…catch往哪里加,throw是在executor函数中抛出的。

  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    console.log(err)
    // 修改 promise对象状态为失败
    reject(err)
  }

在这里插入图片描述

四、状态只能修改一次

  1. resolve:pending —> fulfilled
  2. reject: pending —> rejected
  3. throw XXX 抛出异常: pendg —> rejected
    要确保状态只能修改一次,看看我们上面的代码,假如我同时调用resolve() 和 reject(),你会发现,resolve和reject两个函数都执行了
<script>
  let p = new Promise((resolve, reject) => {
    
    
    resolve('ok')
    reject('error')
  })
  console.log(p)
</script>

在这里插入图片描述

看到吗,resolve和reject只能执行一个的,最后resolve和reject都执行了,这肯定不符合Promise规则,所以代码要修改。
加上判断,如果PromiseState不是pending,那就说明已经被修改了,就不能再修改了

    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 这里的this指向全局window
    // 判断状态 ------------------- 重点
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
  }

  // 声明reject函数
  function reject(data) {
    
    
    // 判断状态 ------------------- 重点
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    console.log(err)
    // 修改 promise对象状态为失败
    reject(err)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    }

五、then方法执行回调

就是实现then方法,在原型链的then方法中调用 onResolved 或者 onRejected方法。什么时候调用呢?根据self.PromseState进行调用。

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  // 调用回调函数 PromiseState
  if (this.PromiseState === 'fulfilled') {
    
    
    onResolved(this.PromiseResult) // 调用时 p.then(value=>{value是参数},reason=>{})
  }
  if (this.PromiseState === 'rejected') {
    
    
    onRejected(this.PromiseResult) // 调用时 p.then(value=>{},reason=>{reason是参数})
  }
}

如果执行resolve(‘ok’)

<script>
  let p = new Promise((resolve, reject) => {
    
    
    resolve('ok')
    // reject('Error1')
    // throw 'Error2'
  })
  console.log(p)
  p.then(value => {
    
    
    console.log(value)
  }, reason => {
    
    
    console.log(reason)
  })
</script>

在这里插入图片描述
如果执行 reject(‘Error1’)
在这里插入图片描述
如果执行 throw ‘Error2’
在这里插入图片描述

function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 这里的this指向全局window
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
  }

  // 声明reject函数
  function reject(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    console.log(err)
    // 修改 promise对象状态为失败
    reject(err)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  // 调用回调函数 PromiseState
  if (this.PromiseState === 'fulfilled') {
    
    
    onResolved(this.PromiseResult) // 调用时 p.then(value=>{value是参数},reason=>{})
  }
  if (this.PromiseState === 'rejected') {
    
    
    onRejected(this.PromiseResult) // 调用时 p.then(value=>{},reason=>{reason是参数})
  }
}

六、异步任务then方法实现

前面讲的是简单的同步实现的情况,在构造函数executor里同步执行 resolve/reject/throw对状态进行修改,然后then函数里根据修改后的状态进行对应调用。
那么问题来了,如果构造函数里的executor里面是异步呢,事实上大部分都是异步的情况,也就是不直接在executor里直接修改Promise的状态,而是异步比如设置定时器,这种情况呢?

<script>
  let p = new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
     // 重点关注
      resolve('ok')
    }, 1000)
  })
  p.then(value => {
    
    
    console.log(value) // 没有执行
  }, reason => {
    
    
    console.log(reason)
  })
</script>

简单分析就知道代码走到then的时候,定时器里的函数还没执行,p的状态还未改变,还是pending,在prototype.then方法里,没有对pending状态的分析判断,所以加个if分支

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  // 调用回调函数 PromiseState
  if (this.PromiseState === 'fulfilled') {
    
    
    onResolved(this.PromiseResult)
  }
  if (this.PromiseState === 'rejected') {
    
    
    onRejected(this.PromiseResult)
  }
  if (this.PromiseState === 'pending') {
    
    
    // ... 重点
  }
}

继续分析,我们在使用Promise的时候,then里面的回调函数应该是在promise状态改变完成之后再执行,但是状态改变是异步(定时器里)的,所以,要等异步执行得到修改后的状态之后再执行后续回调,所以真正调用 成功/失败 的回调应该是在 Promise.js中 resolve/reject 函数中调用,而不是在 then 函数中调用,虽然不能在then函数中调用,但then函数中可以帮助 记录回调函数。
在这里插入图片描述

在这里插入图片描述
在then函数中将成功/失败的回调保存起来,等Promise的状态发生变化后在调用,也就是在resolve/reject里调用。
在这里插入图片描述
现在再通过异步方式,就能得到正确结果了。
总结:上面两节,其中第五节是同步执行,第六节是异步执行。同步执行很简单如下图所示:
在这里插入图片描述
如果是异步的情况,也就是在使用Promise的时候,resolve/reject/throw 的时候是异步的,那么then里的回调函数在源码实现的时候就要保存记录需要执行的回调函数,实际上真正执行的位置是放在构造函数里面的resolve函数和reject函数:
在这里插入图片描述
在这里插入图片描述

七、指定多个回调

如果使用的时候,一个Promise对象有多个then回调,Promise规则是多个回调都要执行,如下所示:
在这里插入图片描述

既然多个回调都要执行,那么上一节写的回调函数就不只是一个对象了,而应该是一个数组,数组里面有多个对象。否则,第N+1个then回调就会把第N个then回调覆盖了。我们应该把所有的then回调都保存下来,具体执行的时候呢,就应该在resolve/reject函数实现中对callbacks进行循环调用。

this.callbacks = []
...
if (this.PromiseState === 'pending') {
    
    
	this.callbacks.push({
    
    
	  onResolved,
      onRejected
	})
}
function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 声明属性,用于保存成功/失败的回调函数,异步情况
  this.callbacks = []
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 调用成功的回调函数 ??? 怎么才能调用到呢?
    self.callbacks.forEach(item => {
    
    
      item.onResolved(data)
    })
  }
  // 声明reject函数
  function reject(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 执行回调
    self.callbacks.forEach(item => {
    
    
      item.onRejected(data)
    })
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    // 修改 promise对象状态为失败
    reject(err)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  // 调用回调函数 PromiseState
  if (this.PromiseState === 'fulfilled') {
    
    
    onResolved(this.PromiseResult)
  }
  if (this.PromiseState === 'rejected') {
    
    
    onRejected(this.PromiseResult)
  }
  if (this.PromiseState === 'pending') {
    
    
    // 保存回调函数
    this.callbacks.push({
    
    
      onResolved,
      onRejected
    })
  }
}

测试环节:

  let p = new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
    
      resolve('ok')
      // reject('err')
    }, 1000)
  })

在这里插入图片描述

  let p = new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
    
      // resolve('ok')
      reject('err')
    }, 1000)
  })

在这里插入图片描述

八、同步任务 then 返回结果

先看一下then的使用

<script>
  let p = new Promise((resolve, reject) => {
    
    
  	resolve('ok')
  })
  const result = p.then(value => {
    
    
    console.log(value)
  }, reason => {
    
    
    console.log(reason)
  })
  console.log(p)
</script>

then返回结果的特点:
then函数返回的结果是由then指定的回调函数的执行结果决定的。

 const result = p.then(value => {
    
    
   console.log(value)
 }, reason => {
    
    
   console.log(reason)
 })

1,假如说then里的回调函数返回一个 非promise 的结果,比如 数字,字符串啊之类的,那么result就是一个成功的promise。
2,假如then里的回调函数返回的是一个promise对象,那么你返回的这个promise就决定了then的返回结果。

<script>
  let p = new Promise((resolve, reject) => {
    
    
    resolve('ok')
  })
  const result = p.then(value => {
    
    
    console.log(value)
  }, reason => {
    
    
    console.log(reason)
  })
  console.log(result)
</script>

看看运行结果(运行的是官方的Promise),返回一个promise,PromiseResult是undefined,那是因为p.then(…),then里的回调函数没有返回语句,其实就是return undefined;假如说then里的回调函数返回一个 非promise 的结果,比如 数字,字符串,undefined啊之类的,那么result就是一个成功的promise。记着这句话。
在这里插入图片描述
那么怎么实现then的返回呢?
首先,then返回的是一个Promise,所以实现肯定是return new Promise(…)。
p.then(…)里面的回调
1、返回非Promise对象
2、返回Promise对象,又分为resolve和reject
3、throw 异常
我们要对这三种情况分别进行源码实现处理
看实现之后的源码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>我是Promise</title>
    <script src="./Promise.js"></script>
  </head>
  <body>
<script>
  let p = new Promise((resolve, reject) => {
      
      
    resolve('ok')
  })
  const result = p.then(value => {
      
      
  	// 下面是几种测试代码,用于测试几种不同的情况
    return 'hello'
    // return new Promise((resolve, reject) => {
      
      
    //   // resolve('success')
    //   reject('No')
    // })
    // throw 'failed'
  }, reason => {
      
      
    console.log(reason)
  })
  console.log(result)
</script>
  </body>
</html>
function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 声明属性,用于保存成功/失败的回调函数,异步情况
  this.callbacks = []
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 调用成功的回调函数 ??? 怎么才能调用到呢?
    self.callbacks.forEach(item => {
    
    
      item.onResolved(data)
    })
  }
  // 声明reject函数
  function reject(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 执行回调
    self.callbacks.forEach(item => {
    
    
      item.onRejected(data)
    })
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    // 修改 promise对象状态为失败
    reject(err)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  return new Promise((resolve, reject) => {
    
    
    // 调用回调函数 PromiseState
    if (this.PromiseState === 'fulfilled') {
    
    
      let result = onResolved(this.PromiseResult)
      // 判断
      if (result instanceof Promise) {
    
    
        result.then(v => {
    
    resolve(v)}, r => {
    
    reject(v)})
      } else {
    
    
        resolve(result)
      }
    }
    if (this.PromiseState === 'rejected') {
    
    
      onRejected(this.PromiseResult)
    }
    if (this.PromiseState === 'pending') {
    
    
      // 保存回调函数
      this.callbacks.push({
    
    
        onResolved,
        onRejected
      })
    }
  })
}

在这里插入图片描述
值得一提的是,如果不是return,而是throw一个异常,那么我们该怎么处理呢?
显然是用try…catch处理
在这里插入图片描述
如下图用throw来测试一下代码
在这里插入图片描述
测试结果:
在这里插入图片描述

九、异步任务then返回结果

上面一节讲了同步的情况,现在看看异步情况。

let p = new Promise((resolve, reject) => {
    
    
	// 模拟异步情况
	setTimeout(() => {
    
    
		resolve('ok')
	}, 1000)
})

我们先用官方的Promise
在这里插入图片描述

在这里插入图片描述
再用自己实现的Promise
测试结果如下:
在这里插入图片描述
诶?奇怪了,为啥明明我一秒后resolve(‘ok’)了,但结果却是pending和null。
在这里插入图片描述
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>我是Promise</title>
    <script src="./Promise.js"></script>
  </head>
  <body>
<script>
  let p = new Promise((resolve, reject) => {
      
      
    setTimeout(() => {
      
      
      // resolve('ok')
      reject('no')
    }, 1000)
  })
  const result = p.then(value => {
      
      
    return '123'
    // return new Promise(...)
    // throw 'error'
  }, reason => {
      
      
    // return '123'
    // return new Promise(...)
    throw 'error'
  })
  console.log(result)
</script>
  </body>
</html>
function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 声明属性,用于保存成功/失败的回调函数,异步情况
  this.callbacks = []
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 调用成功的回调函数 ??? 怎么才能调用到呢?
    self.callbacks.forEach(item => {
    
    
      item.onResolved(data)
    })
  }
  // 声明reject函数
  function reject(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 执行回调
    self.callbacks.forEach(item => {
    
    
      item.onRejected(data)
    })
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    // 修改 promise对象状态为失败
    reject(err)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  const self = this
  return new Promise((resolve, reject) => {
    
    
    // 调用回调函数 PromiseState
    if (this.PromiseState === 'fulfilled') {
    
    
      try {
    
    
        let result = onResolved(this.PromiseResult)
        // 判断
        if (result instanceof Promise) {
    
    
          result.then(v => {
    
    resolve(v)}, r => {
    
    reject(v)})
        } else {
    
    
          resolve(result)
        }
      } catch (error) {
    
    
        reject(error)
      }
    }
    if (this.PromiseState === 'rejected') {
    
    
      onRejected(this.PromiseResult)
    }
    if (this.PromiseState === 'pending') {
    
    
      // 保存回调函数
      this.callbacks.push({
    
    
        onResolved: function() {
    
    
          try {
    
    
            let result = onResolved(self.PromiseResult)
            if (result instanceof Promise) {
    
    
              result.then(v => {
    
    resolve(v)}, r => {
    
    reject(v)})
            } else {
    
    
              resolve(result)
            }
          } catch (err) {
    
    
            reject(err)
          }
        },
        onRejected: function() {
    
    
          try {
    
    
            let result = onRejected(self.PromiseResult)
            if (result instanceof Promise) {
    
    
              result.then(v => {
    
    resolve(v)}, r => {
    
    reject(v)})
            } else {
    
    
              resolve(result)
            }
          } catch (err) {
    
    
            reject(err)
          }
        }
      })
    }
  })
}

十、上述代码的优化

上面有很多重复代码,需要优化,封装。
先看看上面的代码,then方法的实现中,当PromiseState为同步rejected的时候,还没做多情况处理,套路都是一样,还是分为 promise,非promise,throw几种情况

if (this.PromiseState === 'rejected') {
    
    
  try {
    
    
    let result = onRejected(this.PromiseResult)
    if (result instanceof Promise) {
    
    
      result.then(v=>{
    
    resolve(v)}, r=>{
    
    reject(r)})
    } else {
    
    
      resolve(result)
    }
  } catch (error) {
    
    
    reject(error)
  }
}

修改后的代码:

function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 声明属性,用于保存成功/失败的回调函数,异步情况
  this.callbacks = []
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 调用成功的回调函数 ??? 怎么才能调用到呢?
    self.callbacks.forEach(item => {
    
    
      item.onResolved(data)
    })
  }
  // 声明reject函数
  function reject(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 执行回调
    self.callbacks.forEach(item => {
    
    
      item.onRejected(data)
    })
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    // 修改 promise对象状态为失败
    reject(err)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  const self = this
  return new Promise((resolve, reject) => {
    
    
    function callback(type) {
    
    
      try {
    
    
        let result = type(self.PromiseResult)
        // 判断
        if (result instanceof Promise) {
    
    
          result.then(v => {
    
    resolve(v)}, r => {
    
    reject(r)})
        } else {
    
    
          resolve(result)
        }
      } catch (error) {
    
    
        reject(error)
      }
    }
    // 调用回调函数 PromiseState
    if (this.PromiseState === 'fulfilled') {
    
    
      callback(onResolved)
    }
    if (this.PromiseState === 'rejected') {
    
    
      callback(onRejected)
    }
    if (this.PromiseState === 'pending') {
    
    
      // 保存回调函数
      this.callbacks.push({
    
    
        onResolved: function() {
    
    
          callback(onResolved)
        },
        onRejected: function() {
    
    
          callback(onRejected)
        }
      })
    }
  })
}

十一、catch方法与异常穿透

Promise.prototype.catch = function(onRejected) {
    
    
  return this.then(undefined, onRejected)
}

那么如何穿透呢?所谓穿透,就是最后一个catch,前面的错误或者异常无论隔多少层个then,都能穿透到最后那个catch中

<script>
  let p = new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
    
      reject('no')
    }, 1000)
  })
  const result = p.then(value => {
    
    
    console.log(111)
  }).then(value => {
    
    
    console.log(222)
  }).then(value => {
    
    
    console.log(333)
  }).catch(r => {
    
    
    console.log(r)
  })
  console.log(result)
</script>

我们看看原生Promise运行结果:
在这里插入图片描述
尽管隔了好几层then,可还是被catch捕捉了。
那我们自己写的呢?
会报一个错误,onRejected is not a function。为什么呢?
在这里插入图片描述
而且官方的Promise中then()函数是两个参数都不传也可以,而我们实现的Promise默认认为两个参数都传了,在代码中直接就用了,所以一旦遇到不传的时候就报错了,算是一个容错问题。
在这里插入图片描述
在这里插入图片描述

十二、Promise.resolve函数实现

我们在使用Promise的时候会用到Promise.resolve,可见resolve并不是原型链上的函数,属于Promise函数对象本身的一个方法。
Promise.resolve(传值) 返回的是一个Promise,它的状态由传入的值来决定。如果传入的是一个非Promise类型数据,那么返回就是一个成功的Promise,而且传入的参数就是返回的Promise的成功的结果值;如果你传入的是一个Promise类型的值,那么返回的状态和值就是你传入的Promise的状态和结果来决定。

Promise.resolve = function(value) {
    
    
  return new Promise((resolve, reject) => {
    
    
    if (value instanceof Promise) {
    
    
      value.then(v => {
    
    resolve(v)}, r => {
    
    reject(r)})
    } else {
    
    
      resolve(value)
    }
  })
}

十三、Promise.reject函数的实现

Promise.reject()无论你里面传任何参数,返回的永远都是一个失败的Promise,传什么都不好使。看看官方内置的Promise的运行结果

<script>
  let p = Promise.reject('error')
  let p2 = Promise.reject(new Promise((resolve, reject) => {
    
    
    resolve('OK')
  }))
  console.log(p)
  console.log(p2)
</script>

在这里插入图片描述
我们来实现自己的Promise.reject()函数,因为无论如何都是返回失败的Promise,所以实现起来比resolve方便,不用判断了
在这里插入图片描述
在这里插入图片描述

十四、Promise.all()方法的封装

看看下面的代码实现,错在哪里?

Promise.all = function(promises) {
    
    
  return new Promise((resolve, reject) => {
    
    
    for (let i = 0; i < promises.length; i++) {
    
    
      promises[i].then(v => {
    
    
        resolve(v)
      }, r => {
    
    
        reject(r)
      })
    }
  })
}

其实应该是n个promises都成功才能执行resolve函数,而不是有一个成功就resolve了。

function Promise(executor) {
    
    
  this.PromiseState = 'pending'
  this.PromiseResult = null
  // 声明属性,用于保存成功/失败的回调函数,异步情况
  this.callbacks = []
  // 保存实例对象 this 的值,这里的this指向以后对象
  const self = this
  // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
  function resolve(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'fulfilled'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 调用成功的回调函数 ??? 怎么才能调用到呢?
    self.callbacks.forEach(item => {
    
    
      item.onResolved(data)
    })
  }
  // 声明reject函数
  function reject(data) {
    
    
    // 判断状态
    if (self.PromiseState !== 'pending') {
    
    
      return
    }
    // 1. 修改对象状态 PromiseState
    self.PromiseState = 'rejected'
    // 2. 设置对象结果值 PromiseResult
    self.PromiseResult = data
    // 执行回调
    self.callbacks.forEach(item => {
    
    
      item.onRejected(data)
    })
  }

  // 同步调用「执行器函数」
  // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
  // 还要对 resolve 和 reject 进行声明
  try {
    
    
    executor(resolve, reject)
  } catch (err) {
    
    
    // err 就是使用的时候throw抛出的错误
    // 修改 promise对象状态为失败
    reject(err)
  }
}

Promise.prototype.then = function(onResolved, onRejected) {
    
    
  const self = this
  if (typeof onRejected !== 'function') {
    
    
    // 如果不传递onRejected,我们给个默认处理,抛出异常
    // 这个容错处理,不仅解决了undefined问题,更是解决了穿透问题
    // 相当于是给每个then提供了一个默认的onRejected函数
    onRejected = reason => {
    
    
      throw reason
    }
  }
  // 同理,如果使用的时候没传onResolved,我们也可以指定一个默认的处理方式
  if (typeof onResolved !== 'function') {
    
    
    onResolved = value => value
  }
  return new Promise((resolve, reject) => {
    
    
    function callback(type) {
    
    
      try {
    
    
        let result = type(self.PromiseResult)
        // 判断
        if (result instanceof Promise) {
    
    
          result.then(v => {
    
    resolve(v)}, r => {
    
    reject(r)})
        } else {
    
    
          resolve(result)
        }
      } catch (error) {
    
    
        reject(error)
      }
    }
    // 调用回调函数 PromiseState
    if (this.PromiseState === 'fulfilled') {
    
    
      callback(onResolved)
    }
    if (this.PromiseState === 'rejected') {
    
    
      callback(onRejected)
    }
    if (this.PromiseState === 'pending') {
    
    
      // 保存回调函数
      this.callbacks.push({
    
    
        onResolved: function() {
    
    
          callback(onResolved)
        },
        onRejected: function() {
    
    
          callback(onRejected)
        }
      })
    }
  })
}

Promise.prototype.catch = function(onRejected) {
    
    
  return this.then(undefined, onRejected)
}

Promise.resolve = function(value) {
    
    
  return new Promise((resolve, reject) => {
    
    
    if (value instanceof Promise) {
    
    
      value.then(v => {
    
    resolve(v)}, r => {
    
    reject(r)})
    } else {
    
    
      resolve(value)
    }
  })
}

Promise.reject = function(reason) {
    
    
  return new Promise((resolve, reject) => {
    
    
    reject(reason)
  })
}

Promise.all = function(promises) {
    
    
  return new Promise((resolve, reject) => {
    
    
    let count = 0
    let arr = []
    for (let i = 0; i < promises.length; i++) {
    
    
      promises[i].then(v => {
    
    
        count++
        // 将成功的promise放在数组里,注意是按顺序放,i要对准
        // 不要arr.push(v),因为各个promise返回的顺序不一定是原来的顺序
        arr[i] = v
        if (count === promises.length) {
    
    
          resolve(v)
        }
      }, r => {
    
    
        reject(r)
      })
    }
  })
}

十五、Promise.race方法

race简单,几个promise,哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。实现代码中循环执行,就算后面的promise返回了,虽然能执行,但是已经无法修改promise的状态了,因为前面的已经修改了。

Promise.race = function(promises) {
    
    
  return new Promise((resolve, reject) => {
    
    
    for (let i = 0; i < promises.length; i++) {
    
    
      promises[i].then(v => {
    
    
        resolve(v)
      }, r => {
    
    
        reject(r)
      })
    }
  })
}

十六、then方法回调的异步执行

在这里插入图片描述
此节略。

十七、改装成class版本

class Promise {
    
    
  constructor(executor) {
    
    
    this.PromiseState = 'pending'
    this.PromiseResult = null
    // 声明属性,用于保存成功/失败的回调函数,异步情况
    this.callbacks = []
    // 保存实例对象 this 的值,这里的this指向以后对象
    const self = this
    // 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
    function resolve(data) {
    
    
      // 判断状态
      if (self.PromiseState !== 'pending') {
    
    
        return
      }
      // 1. 修改对象状态 PromiseState
      self.PromiseState = 'fulfilled'
      // 2. 设置对象结果值 PromiseResult
      self.PromiseResult = data
      // 调用成功的回调函数 ??? 怎么才能调用到呢?
      self.callbacks.forEach(item => {
    
    
        item.onResolved(data)
      })
    }
    // 声明reject函数
    function reject(data) {
    
    
      // 判断状态
      if (self.PromiseState !== 'pending') {
    
    
        return
      }
      // 1. 修改对象状态 PromiseState
      self.PromiseState = 'rejected'
      // 2. 设置对象结果值 PromiseResult
      self.PromiseResult = data
      // 执行回调
      self.callbacks.forEach(item => {
    
    
        item.onRejected(data)
      })
    }
  
    // 同步调用「执行器函数」
    // 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
    // 还要对 resolve 和 reject 进行声明
    try {
    
    
      executor(resolve, reject)
    } catch (err) {
    
    
      // err 就是使用的时候throw抛出的错误
      // 修改 promise对象状态为失败
      reject(err)
    }
  }

  then(onResolved, onRejected) {
    
    
    const self = this
    if (typeof onRejected !== 'function') {
    
    
      // 如果不传递onRejected,我们给个默认处理,抛出异常
      // 这个容错处理,不仅解决了undefined问题,更是解决了穿透问题
      // 相当于是给每个then提供了一个默认的onRejected函数
      onRejected = reason => {
    
    
        throw reason
      }
    }
    // 同理,如果使用的时候没传onResolved,我们也可以指定一个默认的处理方式
    if (typeof onResolved !== 'function') {
    
    
      onResolved = value => value
    }
    return new Promise((resolve, reject) => {
    
    
      function callback(type) {
    
    
        try {
    
    
          let result = type(self.PromiseResult)
          // 判断
          if (result instanceof Promise) {
    
    
            result.then(v => {
    
    resolve(v)}, r => {
    
    reject(r)})
          } else {
    
    
            resolve(result)
          }
        } catch (error) {
    
    
          reject(error)
        }
      }
      // 调用回调函数 PromiseState
      if (this.PromiseState === 'fulfilled') {
    
    
        callback(onResolved)
      }
      if (this.PromiseState === 'rejected') {
    
    
        callback(onRejected)
      }
      if (this.PromiseState === 'pending') {
    
    
        // 保存回调函数
        this.callbacks.push({
    
    
          onResolved: function() {
    
    
            callback(onResolved)
          },
          onRejected: function() {
    
    
            callback(onRejected)
          }
        })
      }
    })
  }

  catch(onRejected) {
    
    
    return this.then(undefined, onRejected)
  }
  
  resolve = function(value) {
    
    
    return new Promise((resolve, reject) => {
    
    
      if (value instanceof Promise) {
    
    
        value.then(v => {
    
    resolve(v)}, r => {
    
    reject(r)})
      } else {
    
    
        resolve(value)
      }
    })
  }
  
  static reject(reason) {
    
    
    return new Promise((resolve, reject) => {
    
    
      reject(reason)
    })
  }
  
  static all(promises) {
    
    
    return new Promise((resolve, reject) => {
    
    
      let count = 0
      let arr = []
      for (let i = 0; i < promises.length; i++) {
    
    
        promises[i].then(v => {
    
    
          count++
          // 将成功的promise放在数组里,注意是按顺序放,i要对准
          // 不要arr.push(v),因为各个promise返回的顺序不一定是原来的顺序
          arr[i] = v
          if (count === promises.length) {
    
    
            resolve(v)
          }
        }, r => {
    
    
          reject(r)
        })
      }
    })
  }
  
  static race(promises) {
    
    
    return new Promise((resolve, reject) => {
    
    
      for (let i = 0; i < promises.length; i++) {
    
    
        promises[i].then(v => {
    
    
          resolve(v)
        }, r => {
    
    
          reject(r)
        })
      }
    })
  }
}

猜你喜欢

转载自blog.csdn.net/GY_U_YG/article/details/124298718
今日推荐