网站建设前的规划,广水网页定制,自己如何开网站,外国人做的关于中国的视频网站吗目录 构造函数resolve与reject状态改变状态改变后就无法再次改变 代码优化回调函数中抛出错误 thenonFulfilled和onRejected的调用时机异步then多个then 如果是不知道或者对Promise不熟悉的铁铁可以先看我这篇文章 Promise
构造函数
在最开始#xff0c;我们先不去考虑Promi… 目录 构造函数resolve与reject状态改变状态改变后就无法再次改变 代码优化回调函数中抛出错误 thenonFulfilled和onRejected的调用时机异步then多个then 如果是不知道或者对Promise不熟悉的铁铁可以先看我这篇文章 Promise
构造函数
在最开始我们先不去考虑Promise内部是怎么实现而是先将自己的Promise声明出来这里我使用ES6的class来声明
class MyPromise {}在我们new一个Promise的时候会传入一个回调函数这个回调函数有两个形参一个resolve一个reject这个函数将交给Promise来立即执行所以我们的constructor可以这么写
class MyPromise {constructor(func) {func(resolve, reject)}
}值得注意的是Promise本身就是一个任务而回调函数表示的是任务的执行过程所以constructor中的形参应该叫executor而不是func
class MyPromise {constructor(executor) {executor(resolve, reject)}
}resolve与reject
resolve和reject也是函数那么这两个函数定义在哪呢有2种方案 定义在constructor中 constructor(executor) {const resolve (data) {}const reject (reason) {}executor(resolve, reject)
}将其变为原型方法 class MyPromise {
constructor(executor) {func(this.#resolve, this.#reject)
}
#reject(reason) { }
#resolve(data) { }
}因为这个函数我们只会在类的内部使用并不希望用户能在外部访问所以我们将它定义为私有成员 只不过这么写的话会有this的指向问题我们需要使用强制绑定来将函数绑定到正确的地方 class MyPromise {
constructor(executor) {func(this.#resolve.call(this), this.#reject.call(this))
}
#reject(reason) { }
#resolve(data) { }
}这里我选择第一种方法
状态改变
现在我们声明了resolve与reject两个函数但具体这两个函数做什么我们并不清楚事实上这两个函数做的都是同一件事改变当前Promise实例的状态与值只不过resolve是将当前实例的状态改为fulfilled而reject是将当前实例的状态改为rejected明白了这一点我们就能写出如下代码
class MyPromise {#state pending#value nullconstructor(executor) {const resolve (data) {this.#state fulfilledthis.#value data}const reject (reason) {this.#state rejectedthis.#value reason}executor(resolve, reject)}
}我们声明了两个私有属性无论是state还是value我们都不希望用户能从外部访问state用于记录当前实例的状态而value用于记录当前实例得到的值
状态改变后就无法再次改变
这么写就完了吗当然没有在Promise中状态一旦确定就不能再更改反映到代码层面就是无论是在回调函数中写多少个resolve和rejectPromise都只会执行第一个而我们的Promise中目前并没有实现这个功能
const resolve (data) {if (this.#state ! pending) returnthis.#state fulfilledthis.#value data
}
const reject (reason) {if (this.#state ! pending) returnthis.#state rejectedthis.#value reason
}我们在resolve和reject上都加了一行判断如果当前实例的state不是pending的话就说明状态已经改变不能再继续执行 写到这里我们发现resolve和reject函数中的重复代码有点多所以我们可以将其封装成一个独立的函数
class MyPromise {#state pending#value nullconstructor(executor) {const resolve (data) {this.#changeState(fulfilled, data)}const reject (reason) {this.#changeState(rejected, reason)}executor(resolve, reject)}#changeState(state, value) {if (this.#state ! pending) returnthis.#state statethis.#value value}
}代码优化
现在我们发现在我们的代码中还存在着一些硬编码的部分如状态不应该直接使用字符串而是需要使用变量存起来这样如果以后状态的名称发生改变我们也就只需要更改变量的内容
class MyPromise {#state pending#value nullstatic #PENDING pendingstatic #FULFILLED fulfilledstatic #REJECTED rejectedconstructor(executor) {const resolve (data) {this.#changeState(MyPromise.#FULFILLED, data)}const reject (reason) {this.#changeState(MyPromise.#REJECTED, reason)}executor(resolve, reject)}#changeState(state, value) {if (this.#state ! MyPromise.#PENDING) returnthis.#state statethis.#value value}
}我们将三种状态用变量存起来因为三个状态只会在内部使用而且每个实例都会拥有这三个状态所以我将其定义为静态私有成员
回调函数中抛出错误
现在大部分问题我们都解决了但是在回调函数中抛出错误的情况我们并没有处理在Promise中如果回调函数中抛出了错误会被Promise内部捕获到直接reject那么我们的代码就可以这么写
constructor(executor) {const resolve (data) {this.#changeState(MyPromise.#FULFILLED, data)}const reject (reason) {this.#changeState(MyPromise.#REJECTED, reason)}try {executor(resolve, reject)} catch (error) {reject(error)}
}至此我们就将MyPromise的构造器部分完成了
then
在PromiseA规范中通篇都在说什么是Promise简单地说就是Promise可以是一个对象或者是函数但无论是什么都必须要有then方法如果有then方法那就是Promise 所以then方法是Promise中的核心同时也是手写Promise中最难的一部分如果能将then方法手写出来那整个Promise就可以算是大部分完成了 我们回忆一下Promise中的then方法发现then方法会传入两个参数一个是成功时的回调函数一个是失败时的回调函数那我们可以这么定义
class MyPromise {then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) {})}
}因为then方法是每个实例都拥有并且用到的所以我们将其定义为成员方法为了实现Promise的链式调用所以then方法必须返回一个Promise那么在这个返回的Promise中我们究竟该做些什么呢
onFulfilled和onRejected的调用时机
onFulfilled和onRejected什么时候调用这个问题很好解决依据当前Promise的状态判断是调用onFulfilled还是onRejected
then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) {if (this.#state MyPromise.#FULFILLED) onFulfilled(this.#value)if (this.#state MyPromise.#REJECTED) onRejected(this.#value)})
}这么写似乎并没有什么问题那我们来测试一下
let p1 new MyPromise((resolve, reject) {resolve(123)
})
let p2 new MyPromise((resolve, reject) {setTimeout(() {resolve(456)}, 1000)
})
p1.then(data {console.log(data)
})
p2.then(data {console.log(data)
})看得出来p1成功运行了但p2似乎有点问题因为p2在运行到then的时候p2的状态还是pendingp2的状态会在一秒钟后才改变但then方法早在这之前就调用了所以为了避免这种情况我们需要在状态改变的时候再次调用then方法
异步then
再次调用then方法说起来并不精确我们其实真正想要的并不是调用then方法而是想要在状态改变的时候调用onFulfiled或者onRejected那么第一个问题就来了我们在哪里能知道状态什么时候被改变了答案是changeState changeState是用来改变当前实例的状态的函数当它第一次运行时状态肯定被改变我们只需要在这里调用onFulfilled或者onRejected但是有一个新问题这两个回调函数都是直接传入then中的我们无法在changeState中拿到这两个函数那该怎么办呢我们可以用一个中间变量存储
class MyPromise {#handler {}#changeState(state, value) {if (this.#state ! MyPromise.#PENDING) returnthis.#state statethis.#value valueif (this.#state MyPromise.#FULFILLED) this.#handler.onFulfilled(this.#value)else if (this.#state MyPromise.#REJECTED) this.#handler.onRejected(this.#value)}then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) {if (this.#state MyPromise.#FULFILLED) onFulfilled(this.#value)else if (this.#state MyPromise.#REJECTED) onRejected(this.#value)else this.#handler {onFulfilled,onRejected,resolve,reject}})}
}这样问题就解决了但这里面的重复代码有点多我们可以将其封装成一个函数
class MyPromise {#changeState(state, value) {if (this.#state ! MyPromise.#PENDING) returnthis.#state statethis.#value valuethis.#run()}then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) {this.#handler {onFulfilled,onRejected,resolve,reject}this.#run()})}#run() {if (this.#state MyPromise.#FULFILLED) {this.#handler.onFulfilled(this.#value)}else if (this.#state MyPromise.#REJECTED) {this.#handler.onRejected(this.#value)}}}我们封装了一个run函数这个函数专门用来执行then的回调我们还是用上面那个代码测试 至此异步then问题解决
多个then
有时我们会在一个实例上多次调用then方法在实例的状态改变后这些then方法的回调函数应该继续执行但我们的代码却并没有实现 多个then就意味着handler不是一个对象而是一个数组run方法也不再调用一个handler而是遍历handlers将对应状态的回调函数全都取出来执行
class MyPromise {#handlers []then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) {this.#handlersPush(onFulfilled, onRejected, resolve, reject)this.#run()})}#run() {if (this.#state MyPromise.#PENDING) returnwhile (this.#handlers.length 0) {const handler this.#handlers.shift()if (this.#state MyPromise.#FULFILLED) {handler.onFulfilled(this.#value)}else if (this.#state MyPromise.#REJECTED) {handler.onRejected(this.#value)}}}#handlersPush(onFulfilled, onRejected, resolve, reject) {this.#handlers.push({onFulfilled,onRejected,resolve,reject})}
}我们封装了一个辅助函数用于向handlers放入回调在run中我们会一直在handlers里取出回调执行我们使用以下代码测试
let p1 new MyPromise((resolve, reject) {resolve(123)
})
p1.then(data {console.log(第一个then data)
})
p1.then(data {console.log(第二个then data)
})至此我们的Promise如下
class MyPromise {#state pending#value nullstatic #PENDING pendingstatic #FULFILLED fulfilledstatic #REJECTED rejected#handlers []constructor(executor) {const resolve (data) {this.#changeState(MyPromise.#FULFILLED, data)}const reject (reason) {this.#changeState(MyPromise.#REJECTED, reason)}try {executor(resolve, reject)} catch (error) {reject(error)}}#changeState(state, value) {if (this.#state ! MyPromise.#PENDING) returnthis.#state statethis.#value valuethis.#run()}then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) {this.#handlersPush(onFulfilled, onRejected, resolve, reject)this.#run()})}#run() {if (this.#state MyPromise.#PENDING) returnwhile (this.#handlers.length 0) {const handler this.#handlers.shift()if (this.#state MyPromise.#FULFILLED) {handler.onFulfilled(this.#value)}else if (this.#state MyPromise.#REJECTED) {handler.onRejected(this.#value)}}}#handlersPush(onFulfilled, onRejected, resolve, reject) {this.#handlers.push({onFulfilled,onRejected,resolve,reject})}
}因为内容过多所以我将文章分为两篇接下来的部分请看我的这篇文章 js手写Promise下