一步一步手写promise[一]

深藏阁楼爱情的钟 2022-02-17 06:43 491阅读 0赞

开始之前,假设你对promise有个大致的了解,或者快速读一下Promise A+ 规范,过一下即可,对promise熟练使用,那直接跳过吧。可以边实现,边阅读就行。 不保证实现全部遵循这个规范,但是大致流程肯定要按照这个规范来。

我们看一个基础的原生promise

  1. var p1 = new Promise((resolve, reject) => { })
  2. var p2 = new Promise((resolve , reject) => { resolve(1) })
  3. var p3 = new Promise((resolve, reject) => { reject(2) })
  4. console.log(p1, p2, p3)

promise 有 pending rejected resolved 3个状态。

promise的状态必须再调用resolve函数之后才变为resolved,必须调用了传入的reject函数才能变为rejected

并且状态不可逆并且只能由pending —> rejected 或者 pending—>resolved

开始撸代码:

  1. function Promise1(executor) {
  2. this.status = "pending" // resolved rejected pending
  3. // 这里的 executor 里面的两个参数只是外界的构造函数的参数的函数的形式参数
  4. // 比如我们这样写 new Promise((resolve, reject) => { console.log('111') })
  5. // 下面的resolve实际上是在promise内部实现的函数,我们在这里完全可以改为resolve1 reject1
  6. // 这样命名是为了将形式参数,与实际函数内部的实现分开,其实你完全可以写成一样的,但是那样只会让人头大
  7. executor(resolve1, reject1)
  8. // resolve之后,状态应该用 pending 到 resolved
  9. function resolve1() {
  10. if (this.status === 'pending') {
  11. this.status = 'resolved'
  12. }
  13. }
  14. function reject1() {
  15. if (this.status === 'pending') {
  16. this.status = 'rejected'
  17. }
  18. }
  19. }

到这里我们有一个基础的形式了,于是乎我们这样调用:

  1. var p1 = new Promise1((resolve, reject) => {
  2. resolve(1)
  3. })
  4. console.log(p1) // oops,status没有变化,发现问题了吗?

是的,resolve1函数和reject1函数里面this对象并不是Promise1实例,错了几次这,这个错误专门记下来 ( ╯□╰ ),当然你可以使用ES6语法或者用临时变量来避免类似错误,我们直接修改成箭头函数吧:

  1. function Promise2(executor) {
  2. this.status = 'pending'
  3. var resolve1 = () => {
  4. if (this.status === 'pending') {
  5. this.status = 'resolved'
  6. }
  7. }
  8. var reject1 = () => {
  9. if (this.status === 'pending') {
  10. this.status = 'rejected'
  11. }
  12. }
  13. executor(resolve1, reject1) // 形式参数
  14. }
  15. var p2 = new Promise2((resolve, reject) => {
  16. resolve(1)
  17. })
  18. console.log(p2) // 这哈就对了

我们再加一个功能,因为我们一般resolve(),reject()会传递值和错误信息给then的回调。是谁去调用then呢,就是当下的promise;

then方法接收一个两个参数,一个onFulfilled函数和onRejected函数,如果promise从pending->resolved时候resolve(xx)传递了一个值,那么then方法的第一个函数能够获取到,同理reject(yyy)传递的yyy那么then方法的第二个函数也能获取到,如下面那样。

  1. var p1 = new Promise((resolve, reject) => {
  2. resolve(1)
  3. })
  4. p1.then((val)=> { console.log(val)})
  5. var p2 = new Promise((resolve, reject) => {
  6. reject(new Error("something wrong"))
  7. })
  8. p2.then(()=>{ }, (err)=>{ console.error(err)})

那么我们给构造函数增加一个value 和error值,确保实例能够获取到这两个值。

  1. function Promise3(executor) {
  2. this.status = 'pending'
  3. this.error = null
  4. this.value = null
  5. const resolve1 = (resolveValue) =>{
  6. this.value = resolveValue
  7. if (this.status === 'pending'){
  8. this.status = 'resolved'
  9. }
  10. }
  11. const reject1 = (rejectError) => {
  12. this.error = rejectError
  13. if (this.status === 'pending') {
  14. this.status = 'rejected'
  15. }
  16. }
  17. executor(resolve1, reject1)
  18. }
  19. // onFulfilled 为第reolve之后的回调
  20. Promise3.prototype.then = function(onFulfilled, onRejected) {
  21. const { error , value } = this
  22. // 若是同步
  23. // 这里要做一个判断 如果是resolved才去调用onFulfilled, 如果是rejected去调用onRejected
  24. if ( this.status === 'resolved' ) {
  25. onFulfilled(value)
  26. }
  27. if ( this.status === 'rejected' ) {
  28. onRejected(error)
  29. }
  30. }

到这一步就完成了,我们实验一下

  1. var p3 = new Promise3((resolve, reject) => {
  2. resolve(1)
  3. })
  4. console.log(p3, p3.status)
  5. p3.then((val) => console.log(val))
  6. p3.then((val) => console.log(val, "val22"))

达到了我们预期的效果,我们已经实现了这些功能, 构造一个Promise对象 p, 同步或者异步将p的状态从 pendingresolved /rejected。 并且同步执行resolve或者reject函数,能够在then方法里面正确处理状态变化结果的函数,也能够接收到参数。


但是问题来了。。。我们是立即resolve,将promise的状态从pending修改为resolved,但是若是异步的该怎么做呢?
也就是我们必须要保证异步的resolve执行之后,状态才能变为resolved,才能执行then里面的第一个函数。

要实现这样的效果:

  1. var p3 = new Promise((resolve, reject) => {
  2. setTimeout(resolve, 200)
  3. }).then(()=>{
  4. console.log("2s 之后 打印这个信息。。。。")
  5. })

resolve1 和 reject1也可以命名成resolveInner/rejectInner 或者 _reslove / _reject。。只是为了区分实际的参数和形式参数。不想把人绕进去

我们现在的代码是达不到的。。。怎么做呢。。下篇文章见。。

发表评论

表情:
评论列表 (有 0 条评论,491人围观)

还没有评论,来说两句吧...

相关阅读

    相关 地配置Spring

    本文旨在从一个空工程一步一步地配置Spring,空工程见上一篇文章[创建Maven父子工程][Maven]。 \\一、spring基本配置 \\\1. 添加spring依赖