手写Promise--第二篇

前言

上一篇文章,我们已经实现Promise的基本功能和then方法的封装,解决了then方法中存在的异步问题;本篇文章我将带领大家实现then方法的链式调用,若想实现链式调用,首先我们要想到的是,then方法必须要返回一个新的Promise,这样then就能不断的调用,其次前一个then返回的值能够传给后一个then;很显然上一篇文章封装的then方法实现不了, 这两个功能;这篇文章我将带领大家一步步完善then方法,实现then方法的链式调用;
在这里插入图片描述

then的链式调用

前言中已经描述了,要想实现链式调用分为两步, 首先then方法内部要返回一个新的Promise,其次后一个then能接收前一个then的返回值;

实现then方法内部返回一个Promise
在调用then方法时,立即new一个新的Promise实例,然后将实例返回出去,即直接在then方法内部new一个Promise对象,将上一篇文章封装的then方法剪切到新的Promise的处理器函数内部;
代码示例如下:

then(onResolved, onRejected) {
    let promise2 = new Promise((resolve, reject) => {
      // 如果状态为成功, 执行onResolved函数
      if (this.state === 'fulfiled') {
        onResolved(this.value)
      }
      //如果状态为失败,执行onRejected函数
      if (this.state === 'rejected') {
        onRejected(this.reason)
      }

      //如果状态为pending,将回调函数存如数组
      if (this.state === 'pending') {
        //将执行成功的回调函数存入数组
        this.resolvedCallback.push(() => {
          onResolved(this.value)
        })
        //将执行失败的回调函数存入数组
        this.rejectedCallback.push(() => {
          onRejected(this.reason)
        })
      }
    })
    return promise2
  }
}

此时已经实现了每次调用then方法都能返回一个Promise实例,但是上一个then返回的结果和下一个then并没有产生联系,下面带大家继续完善;

then方法内部返回值传给下一个then
实现思路很简单就是将上一个then返回的值作为一个参数传入resolve或reject函数中并调用一下,这样下一个then就能接收上一个then返回的值;代码实例如下:


  then(onResolved, onRejected) {
    let promise2 = new Promise((resolve, reject) => {
      // 如果状态为成功, 执行onResolved函数
      if (this.state === 'fulfiled') {
        const x = onResolved(this.value)
        resolve(x)
      }
      //如果状态为失败,执行onRejected函数
      if (this.state === 'rejected') {
        const x = onRejected(this.reason)
        reject(x)
      }

      //如果状态为pending,将回调函数存如数组
      if (this.state === 'pending') {
        //将执行成功的回调函数存入数组
        this.resolvedCallback.push(() => {
          const x = onResolved(this.value)
          resolve(x)
        })
        //将执行失败的回调函数存入数组
        this.rejectedCallback.push(() => {
          const x = onRejected(this.reason)
          reject(x)
        })
      }
    })
    return promise2
  }

以上便实现了then链式 调用的基本功能;

x的类型判断、循环引用的处理

以上已经实现了then链式的基本功能,但是仅支持返回数值的情况,在实际项目中往往前一个then返回的是一个Promise实例,此时下一个then接收的是一个Promise实例,demo如下:

var p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(4)
    }, 0)
})
p.then((res)=>{
   return new Promise(() => {
	 resolve('ok')
   })
}).then((data) => {
	console.log(data)
})

以上的demo中第一个then()中resolve在Promise实例的执行器函数中,在第二个then的函数中接收的是Promise实例对象;因此我们需要对上一个then返回的值的类型进行判断,如果返回的是Promise对象再调用一下then方法,如果返回的是普通值,直接resolve出去就行了,因为对第一个then返回的值的类型需要判断很多次,因此我们在外部封装一个函数,需要的时候调用就行了;代码示例如下所示:

const resolvePromise = (x, resolve, reject) => {
  //x是Promise实例对象
  if (x instanceof Promise) {
    x.then(
      resolve,
      reject
    )
  } else {
    //x是普通值
    resolve(x)
  }
}

封装好函数后,我们将then方法内部的内部优化一下:

then(onResolved, onRejected) {
    let promise2 = new Promise((resolve, reject) => {
      // 如果状态为成功, 执行onResolved函数
      if (this.state === 'fulfiled') {
        const x = onResolved(this.value)
        resolvePromise(x, resolve, reject)
      }
      //如果状态为失败,执行onRejected函数
      if (this.state === 'rejected') {
        const x = onRejected(this.reason)
        resolvePromise(x, resolve, reject)
      }

      //如果状态为pending,将回调函数存如数组
      if (this.state === 'pending') {
        //将执行成功的回调函数存入数组
        this.resolvedCallback.push(() => {
          const x = onResolved(this.value)
          resolvePromise(x, resolve, reject)
        })
        //将执行失败的回调函数存入数组
        this.rejectedCallback.push(() => {
          const x = onRejected(this.reason)
          resolvePromise(x, resolve, reject)
        })
      }
    })
    return promise2
  }

此时你一定以为then的链式调用已经完成,实际上还存在一个很棘手的问题,即循环引用,循环引用的demo如下图所示
在这里插入图片描述
如上图的then方法返回的值是它本身,这样会导致p2一值等待then方法的返回,造成了循环引用,下面我们将对循环引用进行处理;

循环引用的处理
此时需要 我们在外部封装的函数中对上一个then方法返回的值x进行判断,如果返回的值x全等于新new的promise2,就直接抛出一个循环引用的错误reject出去,代码示例如下:

const resolvePromise = (x, resolve, reject , promise2) => {
  // 处理循环调用问题
  if (x === promise2) {
    console.log(reject)
    return reject(reject(new TypeError('chaining cycle')))
  }
  //x是Promise
  if (x instanceof Promise) {
    x.then(resolve, reject)
  } else {
    //x是普通值
    resolve(x)
  }
}

此时运行后仍然会报错,很显然是因为调用上面的函数是同步的,在调用上面的函数,传入参数时,还拿不到promise2,此时我们需要将调用resolvePromise函数处理成异步的,就能传入promise2这个参数;
代码示例如下:

then(onResolved, onRejected) {
    let promise2 = new Promise((resolve, reject) => {
      // 如果状态为成功, 执行onResolved函数
      if (this.state === 'fulfiled') {
        //将内部代码处理异步,不然拿不到promise2
        setTimeout(() => {
          try {
            let x = onResolved(this.value)
            resolvePromise(x, resolve, reject, promise2)
          } catch (err) {
            reject(err)
          }
        }, 0)
      }
      //如果状态为失败,执行onRejected函数
      if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(x, resolve, reject, promise2) //????????
          } catch (err) {
            reject(err)
          }
        }, 0)
      }

      //如果状态为pending,将回调函数存如数组
      if (this.state === 'pending') {
        //将执行成功的回调函数存入数组
        this.resolvedCallback.push(() => {
          setTimeout(() => {
            try {
              const x = onResolved(this.value)
              resolvePromise(x, resolve, reject, promise2)
            } catch (err) {
              reject(err)
            }
          })
        })
        //将执行失败的回调函数存入数组
        this.rejectedCallback.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              resolvePromise(x, resolve, reject, promise2)
            } catch (err) {
              reject(err)
            }
          })
        })
      }
    })
    return promise2
  }

以上代码就解决了循环引用问题,下面我们来测试一下测试demo如下:

const p = new Promise((resolve, reject) => {
        resolve(22)
})
const p2 = p.then((res) => {
     console.log(res)
     return p2
})

运行代码后控制台打印22 , 并抛出循环引用的错误即成功
在这里插入图片描述
本篇文章只讲到这里,下一篇文章将带领大家对代码进行优化,以及封装Promise的静态方法;

写在最后

🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

手写Promise是一个模拟JavaScript原生Promise的机制,用于处理异步操作的结果。其核心原理主要包括以下几个步骤: 1. **构造函数**:创建一个新的Promise实例,它包含三种状态:pending(等待中)、fulfilled(已成功)和rejected(已失败)。这三个状态是Promise对象的基本属性。 ```javascript function Promise(executor) { let resolve, reject; const promise = { then(onFulfilled, onRejected): Promise, catch(onRejected): Promise, // 等同于then(null, onRejected) state: 'pending', value: undefined, reason: undefined }; try { executor(resolve, reject); } catch (error) { reject(error); } return promise; } ``` 2. **回调函数**:`executor`接收两个函数作为参数,`resolve`用于将Promise从等待状态变为已完成状态并传递结果,`reject`用于将Promise变为已拒绝状态并传递错误。 3. **链式调用**:`then`方法接受两个回调函数,当Promise状态改变时,会依次执行这两个函数。如果当前状态已经是fulfilled,那么就调用第一个回调;如果是rejected,就调用第二个回调(默认的catch方法)。返回的是新的Promise,保持了链式调用的特点。 4. **状态转换**:只有在`resolve`或`reject`内部调用才会改变Promise的状态,外部无法直接修改状态。这样保证了一旦状态确定就不会再变化,提高了代码的可靠性。 ```javascript promise.then((value) => { if (/* 检查状态为fulfilled */) { // 成功处理值 return processValue(value); // 返回另一个Promise } else { throw new Error('处理失败'); } }, (reason) => { // 处理错误 }); ```
评论 73
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端小二哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值