今天我们来自己实现一个promise,首先看下promise的用法
function getData(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
let num = Math.random()
if (num > 0.5) {
resolve('成功了')
} else {
reject('失败了')
}
},1000)
})
}
let p1 = getData()
p1.then(data=>{
console.log('p1成功了吗:'+data)
},err=> {
console.log('p1失败了吗:'+err)
})
复制代码
简易版
根据上面的用法,我们先来实现一个简易版的功能:
- promise 接收一个参数fn,这个参数是个函数
- 参数fn接收两个参数: resolve和reject, 这两个参数也分别是函数
- promise 上有一个then方法,这个方法可接收两个参数,这两个参数也是函数,其实我们可以看做这两个函数是用户传进来的 成功与失败时分别的回调
- promise 意思就是承诺,承诺给你成功了就调用你的成功函数,失败了就调用你的失败函数,那么我们应该给promise一个状态机,这个状态会有一种结果,要么就是成功要么就是失败。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(fn) {
this.status = PENDING; //初始状态
this.value = null;//成功时的值
this.reason = null;//失败时的原因
this.resolveCallBack = null; //用户在then方法中传入的成功回调
this.rejectCallBack = null; //用户在then方法中传入的失败回调
//promise所属的两个函数,为什么要用setTimeout?因为要确保在调用reject 或者 resolve的时候,then 中传入的回调函数已经被注册进来了
this.resolve = (data) =>{
setTimeout(()=>{
if (this.status === PENDING) {
this.status = FULFILLED
this.resolveCallBack(data)
}
},0)
}
this.reject = (err) =>{
setTimeout(()=>{
if (this.status === PENDING) {
this.status = REJECTED
this.rejectCallBack(err)
}
},0)
}
fn.call(undefined,this.resolve,this.reject) //最后 执行一下传入的fn函数
}
// 在then 方法中将用户传入的函数 分别放存放在我们变量中,当异步代码执行完毕之后,
//用户调用我们的resolve或reject方法时,我们就在resolve 或reject里面去调用对应的这两个函数中的一个,并将用户传过来的值再回传给这两个函数中的其中一个,这样在then 中 就可以接收到了
MyPromise.prototype.then = function(resolveCallBack,rejectCallBack){
this.resolveCallBack = resolveCallBack;
this.rejectCallBack = rejectCallBack;
}
复制代码
现在来使用一下我们的promise
function getData(){
return new MyPromise((resolve,reject)=>{
setTimeout(()=>{
if (Math.random() > 0.5) {
resolve('成功了')
} else {
reject('失败了')
}
},1000)
})
}
let p1 = getData()
p1.then(
data=>{
console.log('p1成功了吗?'+data)
},
err=>{
console.log('p1失败了吗?'+err)
}
)
在控制台可以看到随机打印出 'p1成功了吗 成功了'或者'p1失败了吗 失败了'
复制代码
实现同一个promise的多次调用
实现了简易版的promise之后,我们就可以慢慢地完善它了,首先就是 同一个promie我们可以多次调用,就像这样
p1.then(data=>{
console.log('第一次p1成功了吗?'+data)
},err=>{
console.log('第一次p1失败了吗?'+err)
})
p1.then(data=>{
console.log('第二次p1成功了吗?'+data)
},err=>{
console.log('第二次p1失败了吗?'+err)
})
p1.then(data=>{
console.log('第三次p1成功了吗?'+data)
},err=>{
console.log('第三次p1失败了吗?'+err)
})
复制代码
那我们看到这种用法就是 同一个promise对象上注册了很多个回调了,那只要我们在添加回调的地方用一个数组来存储这些回调,然后在执行回调的地方遍历执行就ok了。 下面开始修改我们的代码了(修改的地方我都有标记出来了):
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(fn) {
this.status = PENDING;
this.value = null;
this.reason = null;
// this.resolveCallBack = null;
// this.rejectCallBack = null;
//这里改成数组来存储回调
this.resolveCallBackArray = [];
this.rejectCallBackArray = [];
this.resolve = (data) =>{
setTimeout(()=>{
if (this.status === PENDING) {
this.status = FULFILLED
// this.resolveCallBack(data)
//这里改成遍历回调数组,依次执行回调
this.resolveCallBackArray.forEach(fn =>{
fn(data)
})
}
},0)
}
this.reject = (err) =>{
setTimeout(()=>{
if (this.status === PENDING) {
this.status = REJECTED
// this.rejectCallBack(err)
//这里改成遍历回调数组,依次执行回调
this.rejectCallBackArray.forEach(fn =>{
fn(err)
})
}
},0)
}
fn.call(undefined,this.resolve,this.reject) //最后 执行一下传入的fn函数
}
MyPromise.prototype.then = function(resolveCallBack,rejectCallBack){
// this.resolveCallBack = resolveCallBack;
// this.rejectCallBack = rejectCallBack;
//这里改成将回调依次push进入数组
this.resolveCallBackArray.push(resolveCallBack)
this.rejectCallBackArray.push(rejectCallBack)
}
再执行我们上面的p1
p1.then(data=>{
console.log('第一次p1成功了吗?'+data)
},err=>{
console.log('第一次p1失败了吗?'+err)
})
p1.then(data=>{
console.log('第二次p1成功了吗?'+data)
},err=>{
console.log('第二次p1失败了吗?'+err)
})
复制代码
可以看到 会依次打印出 '第一次p1成功了吗?成功了' ,'第二次p1成功了吗?成功了' 可以自己试试看
到目前为止,我们实现了一个非常简易的prmomise,有任何问题,希望大家能给我指出,接受任何批评!下一篇文章,再来继续完善我们的promise