手写promise步骤详解(基础封装+链式调用)

一、promise 封装

1.基础架构

 function myPromise() {
      let self = this; // 此处 this 代指myPromise 这个函数
      self.status = 'pending' // pending => resolve/reject
      let value = null // 成功时的参数
      let reason = null // 失败时的参数

      
      // 成功的回调
      function resolve(value) {
      }
       // 失败的回调
      function reject(reason) {
      }

2. then方法,定义在原型上

    myPromise.prototype.then = function(onFulfilled, onRejected) {}

3.构造函数立即执行

    let demo = new myPromise((resolve, reject) => {
      console.log('myPromise 完成')
    })

这样呢,一个简单的 promise 框架就写好啦

4.在 promise中添加立即执行函数

function myPromise(excutor) {
   		 ...

      // 4.添加执行器,立即执行
      try {
        excutor(resolve, reject)
      } catch(err) {
        reject(err)
      }
    }

5.编写 resolve\reject 函数

  function myPromise() {
  		 ...
  		 
      // 成功的回调
      function resolve(value) {
      		self.value = value
          self.status = 'fulfilled'
      }
       // 失败的回调
      function reject(reason) {
      		self.value = reason
          self.status = 'rejected'
      }

6.编写 then 函数

myPromise.prototype.then = function(onFulfilled, onRejected) {
	 // 状态改变 就会调用.then 方法,强制要求必须返回一个函数
      onFulfilled = typeof onFulfilled === 'function'  ? 
      onFulfilled : function(data) { resolve(data) }
      onRejected = typeof onRejected === 'function' ?
      onRejected : function(err) { throw err }
}

以上,就实现了一个 promise的基本功能 ,下面我们来执行一下 promise,看是否能正常打印

// 执行构造函数,1秒后输出1
    let demo = new myPromise((resolve, reject) => {
      console.log('myPromise 完成')
      setTimeout(() => {
        resolve(1)
      }, 1000)
    })
 // 调用 promise
 demo.then(res => console.log(res)

我们发现这个结果是不执行的

原因
"demo.then()"方法是同步调用的,此时在 myPromise.prototype.then中打印 this.status 仍然为 pending 状态,失败成功函数都不会执行,所以 demo.then 方法中不执行

解决
此时,我们就可以利用发布-订阅模式来解决此问题,在myPromise.prototype.then方法中,增加保存状态的操作,等到状态改变后再去执行

7.发布订阅模式,支持异步操作

 function myPromise() {
 		……
 		
      self.onFulfilledcallbacks = []
      self.onRejectedcallbacks = []
}

8. 在myPromise.prototype.then添加订阅的操作,先把成功和失败的状态存起来,状态是 pending时候不执行,直到变成 fulfilled 状态再去执行

    myPromise.prototype.then = function(onFulfilled, onRejected) {
    	 ...
      // 8 订阅的操作,先把成功和失败的状态存起来,状态是 pending时候不执行,直到变成 fulfilled 状态再去执行
      let self = this
      if (self.status === 'pending') {
        self.onFulfilledcallbacks.push(onFulfilled)
        self.onRejectedcallbacks.push(onRejected)

      }
    }

9. 在 resolve 、reject 方法中添加发布的操作,当状态改变后再去执行相应的回调

 function myPromise(excutor) {
    	...

     
      // 成功的回调
      function resolve(value) {
        // 5.只有 pending 等待状态才操作
        if(self.status === 'pending')  {
          self.value = value
          self.status = 'fulfilled'
          // 9.状态改变 => 发布,将回调依次取出
          self.onFulfilledcallbacks.forEach(item => item(value))
        }
      }
}
	  // 失败的回调
      function reject(reason) {
        if(self.status === 'pending')  {
          self.value = reason
          self.status = 'rejected'
           // 9.状态改变 => 发布,将回调依次取出
           self.onRejectedcallbacks.forEach(item => item(reason))
        }
      }

接下来,就可以正常打印啦
在这里插入图片描述

全部的代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手写 promise</title>
</head>
<body>
  <h1>手写 Promise</h1>
  <script>
    // 1.基础架构
    function myPromise(excutor) {
      let self = this; // 此处 this 代指myPromise 这个函数
      self.status = 'pending' // pending => resolve/reject
      let value = null // 成功时的参数
      let reason = null // 失败时的参数

      
      // 成功的回调
      function resolve(value) {
        // 5.只有 pending 等待状态才操作
        if(self.status === 'pending')  {
          self.value = value
          self.status = 'fulfilled'
          // 9.状态改变 => 发布,将回调依次取出
          self.onFulfilledcallbacks.forEach(item => item(value))
        }
        console.log('resolve中的回调', self.status)

      }
      // 失败的回调
      function reject(reason) {
        if(self.status === 'pending')  {
          self.value = reason
          self.status = 'rejected'
           // 9.状态改变 => 发布,将回调依次取出
           self.onRejectedcallbacks.forEach(item => item(reason))
        }
      }

      // 7.发布订阅模式,支持异步操作
      self.onFulfilledcallbacks = []
      self.onRejectedcallbacks = []

      // 4.添加执行器,立即执行
      try {
        excutor(resolve, reject)
      } catch(err) {
        reject(err)
      }
    }

    // 2. then方法,定义在原型上
    myPromise.prototype.then = function(onFulfilled, onRejected) {
      // 6.状态改变 就会调用.then 方法,强制要求必须返回一个函数
      onFulfilled = typeof onFulfilled === 'function'  ? 
      onFulfilled : function(data) { resolve(data) }
      onRejected = typeof onRejected === 'function' ?
      onRejected : function(err) { throw err }
      // 未添加发布订阅模式(步骤7)时,状态改变之后,同步去调用 then(demo.then) 方法,但此处 status 仍为 pending,既不会成功也不会失败,所以不会去执行 resolve 方法,状态不同步,所以下方 demo.then 方法不执行
      console.log('同步then 中状态', this.status) 
      // 8 订阅的操作,先把成功和失败的状态存起来,状态是 pending时候不执行,直到变成 fulfilled 状态再去执行
      let self = this
      if (self.status === 'pending') {
        self.onFulfilledcallbacks.push(onFulfilled)
        self.onRejectedcallbacks.push(onRejected)

      }
    }

    // 3.构造函数立即执行
    let demo = new myPromise((resolve, reject) => {
      console.log('myPromise 完成')
      setTimeout(() => {
        resolve(1)
      }, 1000)
    })

    demo.then(res => {
      console.log('demo.then 的执行结果', res)
    })
  </script>
</body>
</html>

二.promise 链式调用

链式调用需要明确以下几点
1).then 中,可以通过把值传给下一步=> 给页面使用
2).也可以返回一个新的 promise,传给下一步
3).为了保证链式调用的实现,上一次then不管成功还是失败,都会把参数作为下一次 then 中成功的参数

10.then 方法中添加 fulfilled链式调用

    if (self.status === 'fulfilled') {
        return new myPromise((resolve, reject) => { // 返回一个 promise
          try {
          	// 重点
          	// 判断成功的回调是否出现在 myPromise构造函数中
          	// instanceof运算符测试构造函数的属性是否出现在prototype对象的原型链中的任何位置
            let x = onFulfilled(self.value)
            // 如果出现就返回新的 Promise.then 否则返回 promise
            x instanceof myPromise ? x.then(resolve, reject): resolve(x)
          } catch(err) {
            reject(err)
          }
        })
      }

11.完善rejected\pending 状态下的链式调用

if (self.status === 'rejected') {
        return new myPromise((resolve, reject) => {
          try {
            let x = onRejected(self.reason)
            x instanceof myPromise ? x.then(resolve, reject): resolve(x)
          } catch(err) {
            reject(err)
          }
        })
  }
      
 if (self.status === 'pending') {
        // self.onFulfilledcallbacks.push(onFulfilled)
        // self.onRejectedcallbacks.push(onRejected)
        return new myPromise((resolve, reject) => {
          self.onFulfilledcallbacks.push(() => {
            let x = onFulfilled(self.value)
            x instanceof myPromise ? x.then(resolve, reject) : resolve(x)
          })

          self.onRejectedcallbacks.push(() => {
            let x = onFulfilled(self.reason)
            x instanceof myPromise ? x.then(resolve, reject) : resolve(x)
          })
        })
      }

12.添加catch 方法

其实就是 then 方法的简化,调用 then 方法,成功函数传入 null,只传入失败函数并执行

  myPromise.prototype.catch = function(fn) {
      return this.then(null, fn)
    }

然后,我们改写一下调用方法

 let demo = new myPromise((resolve, reject) => {
      console.log('myPromise 完成')
      setTimeout(() => {
        let obj = {age: 18}
        resolve(obj)
      }, 1000)
    })

    demo.then(res => {
      let obj = {name: '小明'}
      console.log('第一次调用 then', res)
      return {...obj, ...res};
    }).then(data => {
      console.log('第二次调用 then', data)
    })

打印结果:
在这里插入图片描述
链接调用完事代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手写 promise</title>
</head>
<body>
  <h1>手写 Promise</h1>
  <script>
    // 1.基础架构
    function myPromise(excutor) {
      let self = this; // 此处 this 代指myPromise 这个函数
      self.status = 'pending' // pending => resolve/reject
      let value = null // 成功时的参数
      let reason = null // 失败时的参数

      
      // 成功的回调
      function resolve(value) {
        // 5.只有 pending 等待状态才操作
        if(self.status === 'pending')  {
          self.value = value
          self.status = 'fulfilled'
          // 9.状态改变 => 发布,将回调依次取出
          self.onFulfilledcallbacks.forEach(item => item(value))
        }
        console.log('resolve中的回调', self.status)

      }
      // 失败的回调
      function reject(reason) {
        if(self.status === 'pending')  {
          self.value = reason
          self.status = 'rejected'
           // 9.状态改变 => 发布,将回调依次取出
           self.onRejectedcallbacks.forEach(item => item(reason))
        }
      }

      // 7.发布订阅模式,支持异步操作
      self.onFulfilledcallbacks = []
      self.onRejectedcallbacks = []

      // 4.添加执行器,立即执行
      try {
        excutor(resolve, reject)
      } catch(err) {
        reject(err)
      }
    }

    // 2. then方法,定义在原型上
    myPromise.prototype.then = function(onFulfilled, onRejected) {
      // 6.状态改变 就会调用.then 方法,强制要求必须返回一个函数
      onFulfilled = typeof onFulfilled === 'function'  ? 
      onFulfilled : function(data) { resolve(data) }
      onRejected = typeof onRejected === 'function' ?
      onRejected : function(err) { throw err }
      // 未添加发布订阅模式(步骤7)时,状态改变之后,同步去调用 then(demo.then) 方法,但此处 status 仍为 pending,既不会成功也不会失败,所以不会去执行 resolve 方法,状态不同步,所以下方 demo.then 方法不执行
      console.log('同步then 中状态', this.status) 
      // 8 订阅的操作,先把成功和失败的状态存起来,状态是 pending时候不执行,直到变成 fulfilled 状态再去执行
      let self = this
      if (self.status === 'pending') {
        // self.onFulfilledcallbacks.push(onFulfilled)
        // self.onRejectedcallbacks.push(onRejected)
        return new myPromise((resolve, reject) => {
          // 11.改写 pending 方法
          self.onFulfilledcallbacks.push(() => {
            let x = onFulfilled(self.value)
            x instanceof myPromise ? x.then(resolve, reject) : resolve(x)
          })

          self.onRejectedcallbacks.push(() => {
            let x = onFulfilled(self.reason)
            x instanceof myPromise ? x.then(resolve, reject) : resolve(x)
          })
        })
      }
      
      // 10.链式调用
            //(1)then 中,可以通过把值传给下一步=> 给页面使用
            // (2)也可以返回一个新的 promise,传给下一步
            // (3)为了保证链式调用的实现,上一次then不管成功还是失败,都会把参数作为下一次 then 中成功的参数
            //
      if (self.status === 'fulfilled') {
        return new myPromise((resolve, reject) => {
          try {
            let x = onFulfilled(self.value)
            x instanceof myPromise ? x.then(resolve, reject): resolve(x)
          } catch(err) {
            reject(err)
          }
        })
      }

      if (self.status === 'rejected') {
        return new myPromise((resolve, reject) => {
          try {
            let x = onRejected(self.reason)
            x instanceof myPromise ? x.then(resolve, reject): resolve(x)
          } catch(err) {
            reject(err)
          }
        })
      }
      
    }

    // 12.catch方法
    myPromise.prototype.catch = function(fn) {
      return this.then(null, fn)

    }

    // 3.构造函数立即执行
    let demo = new myPromise((resolve, reject) => {
      console.log('myPromise 完成')
      setTimeout(() => {
        let obj = {age: 18}
        resolve(obj)
      }, 1000)
    })

    demo.then(res => {
      let obj = {name: '小明'}
      console.log('第一次调用 then', res)
      return {...obj, ...res};
    }).then(data => {
      console.log('第二次调用 then', data)
    })



  </script>
</body>
</html>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值