欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

promise 核心封装

程序员文章站 2022-03-21 11:49:07
promise,在项目开发应该大家都不陌生吧,今天我们来看下promise是怎么实现,自己手写一个promise。首先我们来看封装一个promise需要什么条件 let promise = new Promise((resolve,reject) => {}) console.log(promise)由上可知,我们看到promise身上有状态,返回值,__proto__上有一些方法,所以我们可以采用class的方式来创建。// 手写promiseclass XG { // ....

promise,在项目开发应该大家都不陌生吧,今天我们来看下promise是怎么实现,自己手写一个promise。

首先我们来看封装一个promise需要什么条件

 let promise = new Promise((resolve,reject) => {})
 console.log(promise)

promise 核心封装

由上可知,我们看到promise身上有状态,返回值,__proto__上有一些方法,所以我们可以采用class的方式来创建。

// 手写promise
class XG {
  // 属于promise类的静态属性
  static PENDING = 'pending';

  static FULFILLED = 'fulfilled';

  static REJECTED = 'rejected';

  constructor(executor){
    // 设置XG初始化身上的值
    this.status = XG.PENDING
    this.value = null
    // 绑定promise,resolve,reject方法,注意这里this指向问题
    executor(this.resolve.bind(this), this.reject.bind(this))
  }
  // promise 成功捕捉
  resolve(value) {
    console.log(this.status)
    // 设置条件只有在pending状态下才可以改变状态
    if (this.status == XG.PENDING) {
      // 修改status状态
      this.status = XG.FULFILLED
      this.value = value
    }
  }
  // promise 失败捕捉
  reject(value) {
    // 设置条件只有在pending状态下才可以改变状态
    if (this.status == XG.PENDING) {
      // 修改status状态
      this.status = XG.REJECTED
      this.value = value
    }
  }
}

这样就一个简易的promise出来了,接下来是封装then函数。

  // 设置then函数中设置resolve,reject两个函数返回
  then(onFulfilled = () => {}, onRejected  = () => {}) {
    // 设置条件只有在fulfilled状态下才可以改变状态
    if (this.status == XG.FULFILLED) {
      onFulfilled(this.value);
    }
    // 设置条件只有在rejected状态下才可以改变状态
    if (this.status == XG.REJECTED) {
      onRejected(this.value);
    }
  } 

在这就完成我们的then函数基本构造, 引入我们封装的js,改变reject状态可见在浏览器中完美输出。

<script src="./promise.js"></script>
<script>
  new XG((resolve,reject) => {
    // resolve('成功状态')
    reject('失败状态')
  }).then(res => {
    console.log(res)
  },error => {
    console.log(error)
  })
  // let promise = new Promise((resolve,reject) => {})
  // console.log(promise)
</script>

promise 核心封装

现在有一个问题,因为js是单线程执行下来,是会先执行同步,在执行微任务,最后执行宏任务,在promise中,then函数是会被放入微任务队列当中。现在我们封装的then方法是会跟同步一起执行,所以我们需要在then函数中设置一下。

 // 设置then函数中设置resolve,reject两个函数返回
  then(onFulfilled = () => {}, onRejected  = () => {}) {
    // 设置条件只有在fulfilled状态下才可以改变状态
    if (this.status == XG.FULFILLED) {
      // 把任务加入宏任务队列中
      setTimeout(() => {
        // 对我们的then中的错误捕捉
        try {
          onFulfilled(this.value);
        } catch (error) {
          onRejected(error)
        }
      });
    }
    // 设置条件只有在rejected状态下才可以改变状态
    if (this.status == XG.REJECTED) {
      // 把任务加入宏任务队列中
      setTimeout(() => {
        // 对我们的then中的错误捕捉
        try {
          onRejected(this.value);
        } catch (error) {
          onRejected(error)
        }
      })
    }
  }
  let promise = new XG((resolve,reject) => {
    reject('失败状态')
  }).then(res => {
    console.log(res)
  },error => {
    console.log(error,'微任务')
  })
  console.log('同步代码')

promise 核心封装

现在有一个问题是当我们在一秒钟后改变状态的时候,并不会触发then函数中状态捕获,因为,在new 类的时候,then函数已经执行,所以我们需要在then函数中对pending状态捕获。

// 设置一个数组,将未来需要变化的状态函数存放进去
this.callbacks = [];
// 在then中对pending状态捕捉
if (this.status == XG.PENDING) {
  // 当前状态为pending状态时,提前将onFulfilled,onRejected存放进去
  this.callbacks.push({
    // 延迟后对错误的处理
    onFulfilled: value => {
      try {
        onFulfilled(value)
      } catch (error) {
        onRejected(error)
      }
    },
     // 延迟后对错误的处理
    onRejected: value => {
      try {
        onRejected(value)
      } catch (error) {
        onRejected(error)
      }
    }
  })
}
// 当延迟执行resolve函数时,遍历数组,执行状态成功方法
this.callbacks.filter(item => {
   item.onFulfilled(value)
})
// 当延迟执行reject函数时,遍历数组,执行状态成功方法
this.callbacks.filter(item => {
  item.onRejected(value)
})

这样就解决延迟后对状态的捕获。这样还有一个问题,当延时后改变状态时,有同步代码,会先改变状态,所以我们需要把改变状态变为异步操作。

// 状态为padding状态时,加入宏任务队列
setTimeout(() => {
  // 当延迟执行resolve函数时,遍历数组,执行状态成功方法
  this.callbacks.filter(item => {
    item.onRejected(value)
  })
});

// 状态为padding状态时,加入宏任务队列
setTimeout(() => {
  // 当延迟执行reject函数时,遍历数组,执行状态成功方法
  this.callbacks.filter(item => {
    item.onFulfilled(value)
  })
});

promise中的then函数是可以连续点then, then是返回一个新的promise,和之前的promise没有关系, 所以在这,我们的then函数需要进一步封装。

// 设置then函数中设置resolve,reject两个函数返回
  then(onFulfilled = () => {}, onRejected  = () => {}) {
  	// 每次then函数都返回出一个新的pormise实例
    return new XG((resolve,reject) => {
      if (this.status == XG.PENDING) {
        // 当前状态为pending状态时,提前将onFulfilled,onRejected存放进去
        this.callbacks.push({
          // 延迟后对错误的处理
          onFulfilled: value => {
            try {
            // 获取成功状态的返回值,将返回值由下一次成功状态抛出
             let result =  onFulfilled(value)
             resolve(result);
            } catch (error) {
              onRejected(error)
            }
          },
           // 延迟后对错误的处理
          onRejected: value => {
            try {
              // 获取成功状态的返回值,将返回值由下一次成功状态抛出
              let result = onRejected(value)
              resolve(result);
            } catch (error) {
              onRejected(error)
            }
          }
        })
      }
      // 设置条件只有在fulfilled状态下才可以改变状态
      if (this.status == XG.FULFILLED) {
        // 把任务加入宏任务队列中
        setTimeout(() => {
          // 对我们的then中的错误捕捉
          try {
            // 获取成功状态的返回值,将返回值由下一次成功状态抛出
            let result = onFulfilled(this.value);
            resolve(result);
          } catch (error) {
            onRejected(error)
          }
        });
      }
      // 设置条件只有在rejected状态下才可以改变状态
      if (this.status == XG.REJECTED) {
        // 把任务加入宏任务队列中
        setTimeout(() => {
          // 对我们的then中的错误捕捉
          try {
          	// 获取成功状态的返回值,将返回值由下一次成功状态抛出
            let result = onRejected(this.value);
            resolve(result);
          } catch (error) {
            onRejected(error)
          }
        })
      }
    })
  }

让我们引入封装的promise,验证一下。

let promise = new XG((resolve,reject) => {
    resolve('成功')
  }).then(res => {
    console.log(res)
    return 'two2'
  },error => {
    console.log(error,'微任务')
    return '成功'
  }).then(res => {
    console.log('成功:' + res)
  },error => {
    console.log('失败:' + error)

promise 核心封装

这里要注意一点的是,当我们使用原生promise,抛出状态时,then函数中没有写函数,状态值会被下一次then抛出来。

let promise = new Promise((resolve,reject) => {
    resolve('成功')
  })
  .then()
  .then(res => {
    console.log(res)
  },error => {
      console.log(error);
    }
  )

promise 核心封装

所以我们需要给then函数中设置默认函数返回值。

then(onFulfilled = () => this.value, onRejected  = () => this.value) {}

现在有一个问题是原生promise函数.then中可以new 出一个新的实例,下一次then函数中可以获取到这个值。

 let promise = new Promise((resolve,reject) => {
    resolve('成功')
  })
  .then(res => {
    return new Promise((resolve, reject) => {
      resolve('新的promise')
    })
  })
  .then(res => {
    console.log(res)
  })

promise 核心封装

接下来引用我们的promise查看一下。

let promise = new XG((resolve,reject) => {
    resolve('成功')
  })
  .then(res => {
    return new XG((resolve, reject) => {
      resolve('新的promise')
    })
  })
  .then(res => {
    console.log(res);
  })

promise 核心封装

我们的promise中下一次then函数获取到得是整个promise,所以我们需要吧then函数改变一下。

// 在then函数中我们需要对 FULFILLED 和 REJECTED 和 FULFILLED 状态下进行判断
// 判断一下当前返回值是否由XG类实现,是的话他就是一个promise
let result = onFulfilled(this.value);
if (result instanceof XG) {
  // 调用它的then函数,改变状态,把返回值抛出 
  result.then(res => {
    resolve(res)
  }, reason => {
    reject(reason)
  })
} else {
  resolve(result);
}

以上代码有点些繁琐,我们可以简化下

 let result = onFulfilled(this.value);
 // 判断一下当前返回值是否由XG类实现,是的话他就是一个promise
  if (result instanceof XG) {
    // 下面resolve,reject是之前new XG身上的函数,返回新的promise的then会调用这个两个函数,同时也会把参数传入这两个函数。
    result.then(resolve,reject)
  } else {
    resolve(result);
  }
try {
   // 判断一下当前返回值是否由XG类实现,是的话他就是一个promise
    if (result instanceof XG) {
      // 调用它的then函数,改变状态,把返回值抛出
      result.then(resolve,reject)
    } else {
      resolve(result);
    }
  } catch (error) {
    reject(error)
  }

上边的三个状态的代码重复性比较多,我们可以把相同部分提出过,封装成一个函数。

 // 函数复用
  multiplex(result, resolve, reject) {
    // 对我们的then中的错误捕捉
    try {
      // 判断一下当前返回值是否由XG类实现,是的话他就是一个promise
      if (result instanceof XG) {
        // 调用它的then函数,改变状态,把返回值抛出
        result.then(resolve,reject)
      } else {
        resolve(result);
      }
    } catch (error) {
      reject(error)
    }
  }
// PENDING 状态下
// 注意是PENDING 下的value是由上一次resolve传入过来
this.multiplex(onFulfilled(value), resolve, reject);
this.multiplex(onRejected(value), resolve, reject);
// FULFILLED 状态下
this.multiplex(onFulfilled(this.value), resolve, reject);
// REJECTED 状态下
this.multiplex(onRejected(this.value), resolve, reject);

在原生 promise 中,then函数中不允许返回自己本身一样的promise

let promise = new Promise((resolve,reject) => {
    resolve('成功')
  })
  let p =  promise.then(res => {
    return p
  })

promise 核心封装

我们封装的 then 方法没有对这一错误进行捕捉,我们需要重新封装一下

// 把返回新的promise 封装给一个对象
let promise = new XG((resolve,reject) => {}
// 由于是在宏任务当中,是异步执行,可以访问自己,把当前promise对象传入复用函数当中
this.multiplex(promise, onFulfilled(value), resolve, reject);
// 函数复用
multiplex(promise, result, resolve, reject) {
  // 判断是否是自己本身
  if (promise == result) {
    // 主动抛出错误
    throw new TypeError("Chaining cycle detected")
  }
  // 对我们的then中的错误捕捉
  try {
    // 判断一下当前返回值是否由XG类实现,是的话他就是一个promise
    if (result instanceof XG) {
      // 调用它的then函数,改变状态,把返回值抛出
      result.then(resolve,reject)
    } else {
      resolve(result);
    }
  } catch (error) {
    reject(error)
  }
}

在原生promsie当中,是可以直接Promise.resolve(“成功”),进行改变状态,可以在then函数当时获取到resolve当中的值。

 Promise.resolve("成功").then(res =>  {
   console.log(res);
 })
 Promise.reject("失败").then(null,reason =>  {
   console.log(reason);
 })

promise 核心封装

所以我们需要在封装的promise定义两个静态属性方法

// 封装自定属性resolve方法。
  static resolve(value) {
    // 每次调用都是返回一个新的promise实例。
    return new XG((resolve,reject) => {
      // 改变当前的状态,并把传入进来的值抛出去
      resolve(value)
    })
  }
// 引入我们的XG类
XG.resolve("成功").then(res => {
  console.log(res);
})

promise 核心封装

上边是对成功状态的捕捉,现在还有一种情况是当我们在resolve(),传入一个promise,后面的then函数是对传入的promise的状态捕捉,所以我们需要在resolve中判断一下。

 // 封装自定属性resolve方法。
 static resolve(value) {
   // 每次调用都是返回一个新的promise实例。
   return new XG((resolve, reject) => {
     // 判断传入进来的参数是不是一个prosmie,如果是调用它的then函数改变方法,否则直接成功状态返回。
     if (value instanceof XG) {
       value.then(resolve, reject)
     } else {
       // 改变当前的状态,并把传入进来的值抛出去
       resolve(value)
     }
   })
 }
// 引入我们的XG类
let p = new XG((resolve, reject) => {
  reject("失败")
})
XG.resolve(p).then(null, reason => {
  console.log(reason);
})

promise 核心封装

测试成功,接下来是是对reject()函数的封装。

// 封装自定属性reject方法。
  static reject(value) {
    // 每次调用都是返回一个新的promise实例。
    return new XG((resolve, reject) => {
      // 判断传入进来的参数是不是一个prosmie,如果是调用它的then函数改变方法,否则直接成功状态返回。
      if (value instanceof XG) {
        value.then(resolve, reject)
      } else {
        // 改变当前的状态,并把传入进来的值抛出去
        reject(value)
      }
    })
  }

接下来是封装all()方法,promise中的all中是返回一个新的promise,在then()方法中进行获取实例值,这里有一点要注意的是,then() 中捕捉的状态必须统一,否则会进入第二个参数reason当中。

let p1 = new Promise(resolve => {
  resolve("p1-成功")
})
let p2 = new Promise((resolve, reject) => {
  resolve("p2-成功")
})
Promise.all([p1, p2]).then(res => {
  console.log(res)
})

promise 核心封装

接下来是我们的all方法封装。

// 封装自定属性all方法。
 static all(proArr) {
   // 创建resolve数组。
   const values = []
   // 返回一个新的promise实例
   return new XG((resolve, reject) => {
     proArr.forEach(item => {
       item.then(res => {
         // 把当前所有resolve状态下添加进去
         values.push(res)
         // 判断resolve数组添加进来的数量是不是等于传入进来的数量,等于的话,全部resolve状态抛出。
         if (values.length == proArr.length) {
           resolve(values)
         }
         // 如果传入近来有一个reject状态,就会reject状态抛出
       }, resaon => {
         reject(resaon)
       })
     })
   })
 }
let p1 = new XG(resolve => {
  resolve("p1-成功")
})
let p2 = new XG((resolve, reject) => {
  reject("p2-失败")
})
XG.all([p1, p2]).then(res => {
  console.log(res)
}, reason => {
  console.log(reason);
})

promise 核心封装

接下来是对race()函数封装,原生的race方法是对异步传入进来的promise状态,耗时最短的promise优先抛出,后续不在执行。

let p1 = new Promise(resolve => {
  setTimeout(() => {
    resolve("p1-成功")
  }, 1000);
})
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("p2-失败")
  }, 100);
})
Promise.race([p1, p2]).then(res => {
  console.log(res)
}, reason => {
  console.log("reject:" + reason);
})

promise 核心封装

搞清楚race()的特性,接下来使我们的race()函数的封装。

 // 封装自定属性race方法。
 static race(proArr) {
   // 返回一个新的promise实例
   return new XG((resolve, reject) => {
     proArr.forEach(item => {
       item.then(res => {
           // 成功状态抛出
           resolve(res)
       }, resaon => {
         // 失败状态抛出
         reject(resaon)
       })
     })
   })
 }
let p1 = new XG(resolve => {
  setTimeout(() => {
    resolve("p1-成功")
  }, 1000);
})
let p2 = new XG((resolve, reject) => {
  setTimeout(() => {
    reject("p2-失败")
  }, 2000);
})
XG.race([p1, p2]).then(res => {
  console.log(res)
}, reason => {
  console.log("reject:" + reason);
})

promise 核心封装

有对promise底层感兴趣的朋友可以去 后盾人promise学习手册 进行学习。

本文地址:https://blog.csdn.net/Altaird/article/details/110949020