> 文档中心 > 手写Promise--第二篇

手写Promise--第二篇


前言

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

文章の目录

  • 前言
  • then的链式调用
  • x的类型判断、循环引用的处理
  • 写在最后

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的静态方法;

写在最后

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