一步一步手写promise[二]

在上一步,我们已经实现了Promise的基本功能, 构造一个Promise对象,并且可以同步或者异步,将其status转变为 resolved 或者 rejected,并且then方法能正确处理同步的状态变化,但是异步的状态变化我们处理不了。。

回忆一下我们上一步的then方法:

Promise3.prototype.then = function(onFulfilled, onRejected) {
	const  { error , value  }  = this
	// 这里必须要做一个判断 如果是resolved才去调用onFulfilled, 如果是rejected去调用onRejected
	if ( this.status === 'resolved' ) {
		onFulfilled(value)
	}
	if ( this.status === 'rejected' ) {
		onRejected(error)
	}
}

因为我们在执行then函数的时候,此时由于是异步resolved,这个时候Promise对象的状态 并没有变为resolved / rejected 仍然是pending 。所以并不会进入两个if语句执行我们传递的onFulfilled函数和 onRejected函数。

所以我们需要一个对象存储onFulfilled函数,在resolve1函数执行之后去执行这个函数即可。但是请注意因为我们一个Promise对象,可以独立调用多次then函数,多次执行resolve之后的动作,所以我们给构造函数添加两个数组,一个为fulFillTasks ,一个为rejectTasks, 存储传入的函数。

代码长这样:

function Promise4(executor) {
  // 异步队列?
  // 比如在构造函数里面是一个setTimeout,resolve是在下一个事件循环队列再去执行的
  // 这种先后顺序的问题,一般都是放进一个任务队列,前一个执行了 再去触发后一个,参考lazyman
  this.fulFillTasks = []
  this.rejectTasks = []
  this.status = "pending"
  let value = null
  let error = null
  var resolve1 = (resolveValue) => {
    if (this.status === "pending") {
      this.status = "resolved"
      this.value = resolveValue 
      this.fulFillTasks.forEach(fn => {
        fn && fn(resolveValue)
      })
    }
  }

  var reject1 = (rejectError) => {
    if (this.status === "pending") {
      this.status = "rejected"
      this.error = rejectError 
      this.rejectTasks.forEach(fn => {
        fn && fn(rejectError)
      })
    }
  }
  executor(resolve1, reject1)
}

Promise4.prototype.then = function (onFulfilled, onRejected) {
  const value = this.value
  const error = this.error
  //todo 判断 onFulfilled 和 onRejected 是函数
  this.fulFillTasks.push(onFulfilled)
  this.rejectTasks.push(onRejected)
  if (this.status === 'resolved') {
    onFulfilled(this.value)
  }
  if (this.status === 'rejected') {
    onRejected(this.error)
  }
}

这样就完成了这一步,做一个实验

var p = new Promise4((resolve, reject) => {
  resolve('then invoke twice sync')
  // setTimeout(() => {
  //  resolve('then invoke twice async')
  // }, 3000)
})
p.then((v) => {
  console.log('1111', v)
})
p.then(v => {
  console.log('2222', v)
})

在异步resolve之后,才去执行then传递的函数,也做到了。。我们继续往下走


按照promise A+ 规范,或者我们自己看mdn上的then方法解释

then 方法 需要返回一个promise,叫它小p,而且我们都知道在onFulfilled、onRejected回调函数里面返回的值,会作为小p的接收状态、拒绝状态的回调函数的参数值。如果没有返回值,那么就是默认返回了一个undefined,undefined会作为小p的接收状态的回调函数的参数值。【不熟悉的话,自己用原生的Promise做个实验就行了】 我们到这一步暂时考虑返回的是一个promise的情况。

我们还是分两种情况,第一种是立即resolve的promise对象的then的实现【构造函数里面同步调用resolve or reject ,另外就是已经是接受状态的then接受回调函数的实现】,一种是异步resolve的promise对象的then的实现。

我们修改then方法如下:

Promise4.prototype.then = function (onFulfilled, onRejected) {

  // 有两种情况会执行下面的代码 A 和 B
  // 1,构造函数里面传入的resolve 和 reject是同步代码块
  // 2,promise已经是接收状态或者拒绝状态

  // A 执行接收状态的回调函数
  if (this.status === 'resolved') {
    // const fulfillRet = onFulfilled(this.value)
    // onFulfilled可能是在异步resolve后执行,在任务队列里面执行
    // 所以我们这里将这个返回值定义到实例属性上
    this.fulfillRet = onFulfilled(this.value)
    return new Promise4((resolve, reject) => {
      resolve(fulfillRet)
    })
  }
  // B 执行拒绝状态的回调函数
  if (this.status === 'rejected') {
    // const rejectRet = onRejected(this.error)
    this.rejectRet = onFulfilled(this.value)
    return new Promise4((resolve, reject) =>{
      reject(rejectRet)
    })
  }

  // 构造函数里面传入的resolve 和 reject是在异步代码块里面执行
  // 错误
  // this.fulFillTasks.push(onFulfilled)
  // this.rejectTasks.push(onRejected)



  // 遇到了一些苦困难,这里因为是异步的,返回的Promise一开始的状态值肯定是pending
  // 但是如何获取到异步的 onFulfilled的返回值,并且在resolve之后 将这个返回值
  // 传递给返回的这个promise
  // 正确
  return new Promise4((resolve, reject) => {
      // 非常关键,将回调函数和返回的promise的resove函数封装到一个函数里面
      // 然后放到调用then方法的promise的任务队列里面
      // 这样调用then方法的promise 状态从 pending 变为 resolved的时候
      // then方法返回的promise,的状态才会从pending 变为 resolved
      this.fulFillTasks.push((val) => {
        let ret = onFulfilled(val)
        resolve(ret)
      })
      this.rejectTasks.push((err) => {
        let errRet = onRejected(err)
        // 这里也是resolve
        resolve(errRet)
      })
  })
}


到这一步,我们已经可以实现,新new出来的Promise对象异步调用resolve函数改变状态,这个时候then返回的Promise对象随着调用then的Promise对象变化而变化,并且获取到了then回调函数里面的值。

看起来已经完成了,实际上这里犯了一个关键的错误:then回调始终是异步调用的,但是我们的代码却没有体现。

我们拿Promise4 做一个测试,还是根据上面的测试代码来,加一句代码:

	var p = new Promise4((resolve, reject) => {
	  resolve('then invoke twice sync')
	  // setTimeout(() => {
	  //  resolve('then invoke twice async')
	  // }, 3000)
	})
	p.then((v) => {
	  console.log('first resolve callback', v)
	})
	p.then(v => {
	  console.log('second resolve callback', v)
	})
	console.log('sync code in main stack')

你会发现代码打印顺序会是

// first resolve callback
// second resolve callback
// sync code in main stack

实际上期望的应该是:

// sync code in main stack
// first resolve callback
// second resolve callback

所以我们要对代码修改,构造异步执行最好的办法就是setTimeout(fn, time)。
我们修改上面的then方法成下面的样子,构造函数不变:


Promise4.prototype.then = function (onFulfilled, onRejected) {

        // 有两种情况会执行下面的代码 A 和 B
        // 1,构造函数里面传入的resolve 和 reject是同步代码块
        // 2,promise已经是接收状态或者拒绝状态

        // A 执行接收状态的回调函数
        if (this.status === 'resolved') {
          // const fulfillRet = onFulfilled(this.value)
          // onFulfilled可能是在异步resolve后执行,在任务队列里面执行
          // 所以我们这里将这个返回值定义到实例属性上
          setTimeout(() => {
                this.fulfillRet = onFulfilled(this.value)
          }, 0)
          return new Promise4((resolve, reject) => {
            setTimeout(() => {
              resolve(this.fulfillRet)
            }, 0)
          })
        }
        // B 执行拒绝状态的回调函数
        if (this.status === 'rejected') {
          // const rejectRet = onRejected(this.error)
          setTimeout(() => {
            this.rejectRet = onFulfilled(this.value)
          }, 0)
          return new Promise4((resolve, reject) => {
            setTimeout(() => {
              reject(this.rejectRet)
            }, 0)
          })
        }

        // 构造函数里面传入的resolve 和 reject是在异步代码块里面执行
        // 错误
        // this.fulFillTasks.push(onFulfilled)
        // this.rejectTasks.push(onRejected)



        // 遇到了一些困难,这里因为是异步的,返回的Promise一开始的状态值肯定是pending
        // 但是如何获取到异步的 onFulfilled的返回值,并且在resolve之后 将这个返回值
        // 传递给返回的这个promise
        // 正确
        return new Promise4((resolve, reject) => {
            // 非常关键,将回调函数和返回的promise的resove函数封装到一个函数里面
            // 然后放到调用then方法的promise的任务队列里面
            // 这样调用then方法的promise 状态从 pending 变为 resolved的时候
            // then方法返回的promise,的状态才会从 pending 变为 resolved
            // this.fulFillTasks.push((val) => {
            //   let ret = onFulfilled(val)
            //   resolve(ret)
            // })
            // this.rejectTasks.push((err) => {
            //   let errRet = onRejected(err)
            //   // 这里也是resolve
            //   resolve(errRet)
            // })
          var resolveFunc = (val) => {
            return setTimeout(() => {
              let ret = onFulfilled(val)
              resolve(ret)
            }, 0)
          }
          this.fulFillTasks.push(resolveFunc)

          // 函数式编程
          var rejectFunc = (err) => {
            return setTimeout(() => {
              let errRet = onRejected(err)
              resolve(errRet)
            }, 0)
          }
          this.rejectTasks.push(rejectFunc)
        })
      }

然后我们做一次实验:

      var p = new Promise4((resolve, reject) => {
      	  // resolve("kev")
          setTimeout(()=> {
            console.log("2s后执行resolve")
            resolve('resolve')
          }, 2000)
      })
      var y = p.then(()=>{
          console.log("到resolve的回调函数里面了");
          return "kevin"
        }
      )
      console.log("1")
      console.log("p.status", p.status)
      console.log("y.status", y.status)

bingo! 完成了,这一节我们实现了 Promise构造函数里面异步调用resolve/reject,对象状态的变化可以根据异步resolve/reject来控制,异步执行resolve/reject之后,对象的状态才回从pending ------> resolved / rejected。

然后实现了then方法返回一个新的promise,并且这个Promise对象的状态控制也是和调用then方法的对象的状态关联的。


新的问题: 我们上面假定了一个操作是: then方法的接受状态回调onFulfilled和拒绝状态回调onRejected的返回值不是一个Promise对象,但是返回值 是作为then方法 返回的 Promise对象 小p的接收状态和拒绝状态的回调函数的参数值。

这种情况如何处理呢? 下篇文章再见。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值