我们在去学习Promise的时候最先需要知道Promise的三种状态
Promise有三种状态分别是pending fulfilled 还有rejected状态所以在我们去手写Promise的时候可以创建一个对象负责去保管 pending fulfilled rejected
//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
PENDING:0, // pending
FULFILLED:1, // fulfilled
REJECTED:2 // rejected
}
首先我们用class构造函数创建一个MyPromise的类,并且将初始状态设置为pending状态
//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
PENDING:0, // pending
FULFILLED:1, // fulfilled
REJECTED:2 // rejected
}
class MyPromise{
//创建一个变量来存Promise状态
#state = PROMISE_STATE.PENGDING //#是为了保持变量的私有性,不会被修改
//接收一个执行器作为参数
constructor(executor) {
executor(this.#resolve,this.#reject)
}
//设置私有的resolve()用来存储成功的对象
#resolve(value){
console.log('resolve调用了',value)
}
//设置私有的reject()用来存储失败的对象
#reject() { }
}
const mp = new MyPromise((resolve,reject)=>{
resolve('成功的')
})
我们可以从上面写的代码可以看见已经给我的MyPromise构造函数的constructor传了一个回调函数executor也就是我们常说的执行器函数。并且我们为了保证我们的MyPromise对象有一定的安全性,不让轻易修改,这里我们用到了#去对属性和方法都进行了加密。也就是用到了我们面向对象的封装性。
我们在调用resolve的时候可以发现已经传入过去了resolve的值,但是我们Promise对象是要保存数据,我们现在还并没有把数据存起来的能力,所以请看下面代码.
//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
PENDING:0, // pending
FULFILLED:1, // fulfilled
REJECTED:2 // rejected
}
class MyPromise{
//创建一个变量用来存Promise的结果
#result
//创建一个变量来存Promise状态
#state = PROMISE_STATE.PENGDING //#是为了保持变量的私有性,不会被修改
//接收一个执行器作为参数
constructor(executor) {
// executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例
}
//设置私有的resolve()用来存储成功的对象
#resolve(value){
console.log('resolve调用了',value)
this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
}
//设置私有的reject()用来存储失败的对象
#reject() { }
}
const mp = new MyPromise((resolve,reject)=>{
resolve('成功的')
})
上述方法我们给#result赋值会出现错误是因为我们的#resolve在外面是以函数形式调用的所以我们用了bind()方法去改变了#resolve的this指向。当然我们在这直接把#resolve改成箭头函数也可以但是我们这个时候就要注意到,我们在类里面用箭头函数的话,这个方法是在实例对象里面,不再是在原型里面的方法。所以我们在手写Promise的时候去调用resolve或者reject要注意我们的this指向要保持在实例上。但是其实我们又遇到了一个问题,我就只单独说一下。在调用的时候我们Promise对象改变状态是不能够再改变的,也就是Promise只能修改一次。所以我们要为了严谨性要加上一个判断,如果我们已经改变状态了,我们就直接return退出函数
//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
PENDING:0, // pending
FULFILLED:1, // fulfilled
REJECTED:2 // rejected
}
class MyPromise{
//创建一个变量用来存Promise的结果
#result
//创建一个变量来存Promise状态
#state = PROMISE_STATE.PENGDING //#是为了保持变量的私有性,不会被修改
//接收一个执行器作为参数
constructor(executor) {
// executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例
}
//设置私有的resolve()用来存储成功的对象
#resolve(value){
if(this.#state !== PROMISE_STATE.PENGDING ) return
console.log('resolve调用了',value)
this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
this.#state = PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态
}
//设置私有的reject()用来存储失败的对象
#reject() { }
}
const mp = new MyPromise((resolve,reject)=>{
resolve('成功的')
})
上面已经基本完成了存储数据的方法了,现在我们就要开始写用来读取数据的then()方法,有两个参数,一个读取成功的(onFulfilled)一个读取失败的(onRejected)
在这里我们就可以更加清晰的知道这then方法里面的参数和resolve不是同一个东西
//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
PENDING:0, // pending
FULFILLED:1, // fulfilled
REJECTED:2 // rejected
}
class MyPromise{
//创建一个变量用来存Promise的结果
#result
//创建一个变量来存Promise状态
#state = PROMISE_STATE.PENGDING //#是为了保持变量的私有性,不会被修改
//接收一个执行器作为参数
constructor(executor) {
// executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例
}
//设置私有的resolve()用来存储成功的对象
#resolve(value){
if(this.#state !==PROMISE_STATE.PENGDING) return
console.log('resolve调用了',value)
this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
this.#state = PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态
}
//设置私有的reject()用来存储失败的对象
#reject(reason) {
if(this.#state !==PROMISE_STATE.PENGDING) return
this.#result = reason
this.#state = PROMISE_STATE.REJECTED//使用了一次之后就会改变Promise的状态
}
//读取数据的then方法
then(onFulfilled,onRejected){
if(this.#state === PROMISE_STATE.FULFILLED){
onFulfilled(this.#result)
}
}
}
const mp = new MyPromise((resolve,reject)=>{
resolve('成功的')
})
mp.then(result=>{
console.log('读取数据',result)
})
这个样子其实手写promise的雏形已经出来了,但是其实这里还是存在一点小问题,不知道你们能不能看得出来。其实就是如果我们在用异步去执行resolve或者reject的时候是有问题的。那么我们应该怎么去对以上代码进行一系列的操作去完善呢。请听我慢慢分析:首先我们去调用then方法的时候如果我们用异步的方法去执行resolve或者reject我们是还拿不到数据的,为什么呢?因为我们在这里并没有对then方法去做异步的处理,我们只能读取我们存进Promise的数据,不能拿到异步存储的数据,所以接下来我们就是要去解决这个问题了。其实我们主要就是要去判断我们的resolve是不是被调用了,如果resolve被调用我们去调用then就没有问题了
//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
PENDING:0, // pending
FULFILLED:1, // fulfilled
REJECTED:2 // rejected
}
class MyPromise{
//创建一个变量用来存Promise的结果
#result
//创建一个变量来存Promise状态
#state = PROMISE_STATE.PENGDING //#是为了保持变量的私有性,不会被修改
//创建一个变量存储回调函数 也是一个私有属性
#callback
//接收一个执行器作为参数
constructor(executor) {
// executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
executor(this.#resolve.bind(this),this.#reject.bind(this)) //调用回调函数并且将this指向实例
}
//设置私有的resolve()用来存储成功的对象
#resolve(value){
if(this.#state !==PROMISE_STATE.PENGDING) return
console.log('resolve调用了',value)
this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
this.#state = PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态
//当resolve执行时,说明数据已经来了,需要then的回调函数
this.#callback && this.#callback(this.#result)
}
//设置私有的reject()用来存储失败的对象
#reject(reason) {
if(this.#state !==PROMISE_STATE.PENGDING) return
this.#result = reason
this.#state = PROMISE_STATE.REJECTED//使用了一次之后就会改变Promise的状态
}
//读取数据的then方法
then(onFulfilled,onRejected){
//在这里我们还要先做一次判断是pending状态就说明我们还没有执行resolve
if(this.#state === PROMISE_STATE.PENDING){
this.#callback = onFulfilled //将回调函数设置为callback的值
}else if(this.#state === PROMISE_STATE.FULFILLED){
onFulfilled(this.#result)
}
}
}
const mp = new MyPromise((resolve,reject)=>{
resolve('成功的')
})
mp.then(result=>{
console.log('读取数据',result)
})
这个样子我们就可以把异步的也去完成了。但是,但是,但是,重要的事情说三遍,我们Promise的方法是在微任务队列里面的,很明显我们这里就是在调用回调函数而已,并没有达到Promise的then方法在微任务队列遵循事件循环。所以我们需要添加一个函数,queueMicrotask()这个函数就可以做到让then方法放到微任务队列里面了,当然我们的Promise还差一步链式调用所以在最后我们应该把then的方法返回一个新的Promise对象也给加进去
//创建一个对象负责保管这几种状态
const PROMISE_STATE = {
PENDING: 0, // pending
FULFILLED: 1, // fulfilled
REJECTED: 2 // rejected
}
class MyPromise {
//创建一个变量用来存Promise的结果
#result
//创建一个变量来存Promise状态
#state = PROMISE_STATE.PENGDING //#是为了保持变量的私有性,不会被修改
//创建一个变量存储回调函数 也是一个私有属性
//
#callbacks = []
//接收一个执行器作为参数
constructor(executor) {
// executor(this.#resolve,this.#reject) //因为在外面resolve是以函数形式调用所以需要改变resolve的this,不然给#result赋值会出错
executor(this.#resolve.bind(this), this.#reject.bind(this)) //调用回调函数并且将this指向实例
}
//设置私有的resolve()用来存储成功的对象
#resolve(value) {
if (this.#state !== PROMISE_STATE.PENGDING) return
console.log('resolve调用了', value)
this.#result = value // 没改变this指向前这个会报错因为this是undefined所以我们需要改变this指向
this.#state = PROMISE_STATE.FULFILLED //使用了一次之后就会改变Promise的状态
//当resolve执行时,说明数据已经来了,需要then的回调函数
queueMicrotask(() => {
this.#callbacks.forEach(cb => {
cb()
})
})
}
//设置私有的reject()用来存储失败的对象
#reject(reason) {
if (this.#state !== PROMISE_STATE.PENGDING) return
this.#result = reason
this.#state = PROMISE_STATE.REJECTED//使用了一次之后就会改变Promise的状态
}
//读取数据的then方法
then(onFulfilled, onRejected) {
//then中回调函数的返回值,会成为新的Promise中的数据
return new MyPromise(() => {
//在这里我们还要先做一次判断是pending状态就说明我们还没有执行resolve
if (this.#state === PROMISE_STATE.PENDING) {
this.#callback = onFulfilled //将回调函数设置为callback的值
this.#callbacks.push(() => {
resolve(onFulfilled(this.#result))
})
})
} else if (this.#state === PROMISE_STATE.FULFILLE) {
//then的回调函数,放在微任务队列里面
queueMicrotask(() => {
resolve(onFulfilled(this.#result))
})
}
}
})
}
const mp = new MyPromise((resolve, reject) => {
resolve('成功的')
})
mp.then(result => {
console.log('读取数据', result)
})
这里我们自己的Promise对象就搞定了,当然我写的还有很多不足,在后面的学习我会进一步优化然后用es5的方式再写一次