一直以来都对Promise的实现有浓厚的兴趣、感觉很好玩。很想搞清楚他的原理是什么,而最好的办法莫过于写一个自己的Promise了。
首先我们需要看一下Promise的基本使用方式是什么样子的。
let testPromise =new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log(res);
})
可以观测到,在2s后,将会在控制台打印出来ok。
首先,我们要确定的最关键的一点是,Promise没有解决回调函数的问题,可以看到我们的执行方案依旧是根据回调函数来解决的,但是我们的方案更加的优雅一点、其次、如果我们注释后面的代码执行以下代码
let testPromise =new Promise((resolve,reject)=>{
console.log('promise');
setTimeout(()=>{
console.log('ok');
resolve('ok')
},2000)
})
// testPromise.then((res)=>{
// console.log(res);
// })
会很快的打印出来 promise 在过了2s后打印出来OK。可见。Promise是在new的时候就执行了,而不是在then的时候。
同时我们观测我们的代码可以有以下的结论
- Promise是一个类,需要使用new创建
- Promise在new的时候接收一个参数,该参数是一个函数executor
- executor接收两个参数,这两个参数也是一个函数,分别为resolve跟reject
- Promise生成的对象里面有一个属性是then。它接收两个回调函数onFulfilled跟onRejected
- 如果存在异步的情况,那么then中回调函数不会立即执行、可见必有缓存机制
- 在resolve结束之后执行onFulfilled,在reject函数之后执行onRejected,并且只会执行其中一个函数
那么我们按照这个思路来写一个Promise吧
1.
class MyPromise{
}
MyPromise应该是一个类
2.
class MyPromise{
constructor(exector){
exector(resolve,reject)
}
}
可是我们的resolve跟reject在哪里呢?
很明显无法接受外部的参数,那么就只能是我们MyPromise内部的函数了
3.
class MyPromise{
constructor(exector){
exector(resolve,reject)
}
resolve(){
}
reject(){
}
}
class MyPromise{
constructor(exector){
exector(resolve,reject)
}
resolve(){
}
reject(){
}
then(onFulfilled,onRejected){
}
}
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
exector(resolve,reject)
}
resolve(){
}
reject(){
}
then(onFulfilled,onRejected){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
在resolve之后执行onFulfilled 在这里我们就需要引入状态机制了。我们知道Promise是有状态的,而resolve其实就是把Promise的状态从pedding变成fulfilled而reject则是变成rejected
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
this.state='pedding'
this.value=null //成功的回调值
this.reason=null //成功的回调值
exector(resolve,reject)
}
resolve(value){
if(this.state==='pedding'){
this.state='fulfilled'
this.value=value
this.onFulfilledCallbacks.forEach(fn=>fn(value))
}
}
reject(reason){
if(this.value==='pedding'){
this.state='rejected'
this.reason=reason
this.onRejectedCallbacks.forEach(fn=>fn(reason))
}
}
then(onFulfilled,onRejected){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
我们可以测试一下
let testPromise =new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log('then');
console.log(res);
})
可以发现一些问题因为执行环境的问题,所以this的指向是有问题的,我们可以手动绑定一下
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
this.state='pedding'
this.value=null //成功的回调值
this.reason=null //成功的回调值
exector(this.resolve.bind(this),this.reject.bind(this))
}
resolve(value){
if(this.state==='pedding'){
this.state='fulfilled'
this.value=value
this.onFulfilledCallbacks.forEach(fn=>fn(value))
}
}
reject(reason){
if(this.state==='pedding'){
this.state='rejected'
this.reason=reason
this.onRejectedCallbacks.forEach(fn=>fn(reason))
}
}
then(onFulfilled,onRejected){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
这样实际上执行结果就符合我们的预期了
但是其实还是有些问题的
- 不是很符合PromiseA+的规范
- reject跟resolve函数其实不应该被返回
- 是否在一开始的时候就存在fulfilled状态呢?
对于第三个问题,我们注意到Promise被执行的时候并不是在then的时候而是在new的时候,也就是说会有可能出现这种情况
setTimeout(()=>{
testPromise.then((res)=>{
console.log('then');
console.log(res);
})
},3000)
观测到控制台无打印,因为此时已经是fulfilled状态了
因此我们必须把,而因此没有执行then的缘故,所以并没在callbacks里面添加函数,因此我们要在then里面设置一下状态改变
下面给出一版本更规范一点的实现
class MyPromise{
constructor(exector){
this.onFulfilledCallbacks=[]
this.onRejectedCallbacks=[]
this.state='pedding'
this.value=null //成功的回调值
this.reason=null //成功的回调值
let resolve=(value)=>{
if(this.state==='pedding'){
this.state='fulfilled'
this.value=value
this.onFulfilledCallbacks.forEach(fn=>fn(this.value))
}
}
let reject=(reason)=>{
if(this.state==='pedding'){
this.state='rejected'
this.reason=reason
this.onRejectedCallbacks.forEach(fn=>fn(this.reason))
}
}
exector(resolve,reject)
}
then(onFulfilled,onRejected){
if(this.state==='fulfilled'){
onFulfilled(this.value)
}
if(this.state==='rejected'){
onRejected(this.reason)
}
if(this.state==='pedding'){
this.onFulfilledCallbacks.push(onFulfilled)
this.onRejectedCallbacks.push(onRejected)
}
}
}
let testPromise =new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log('then');
console.log(res);
})
关于PromiseA+规范我们可以参考
这样我们就实现了一个基本具有Promise样子的MyPromise。但是。回到开头我们提到了Promise依旧是依靠回调函数来执行的,这个样子跟我们使用回调函数的区别不是很大,为什么还要写Promise呢?原因在于Promise的精髓。链式调用
也就是
let testPromise =new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},2000)
})
testPromise.then((res)=>{
console.log(res);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok2')
},2000)
})
}).then((res)=>{
console.log(res);
})
下一篇文章我们将会研究一下Promise是怎么实现链式调用的