promise 核心封装

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

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

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

promise 核心封装_第1张图片

由上可知,我们看到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 核心封装_第2张图片

现在有一个问题,因为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('同步代码')

在这里插入图片描述

现在有一个问题是当我们在一秒钟后改变状态的时候,并不会触发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 核心封装_第3张图片

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

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

promise 核心封装_第4张图片

所以我们需要给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 核心封装_第5张图片

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

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

promise 核心封装_第6张图片

我们的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
  })

在这里插入图片描述

我们封装的 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定义两个静态属性方法

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

在这里插入图片描述

上边是对成功状态的捕捉,现在还有一种情况是当我们在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);
})

在这里插入图片描述

测试成功,接下来是是对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)
})

在这里插入图片描述

接下来是我们的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);
})

在这里插入图片描述

接下来是对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);
})

在这里插入图片描述

搞清楚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学习手册 进行学习。
项目地址 https://github.com/xiaoguo66/promise

你可能感兴趣的:(js,javascript,js,es6,前端)