按照Promises/A+规范,手写一个Promise
首先第一步:
Promise代码基本结构
先写一个简单的promise调用
const Promise = require('./promise4.js')
function a(){
return new Promise((resolve,reject)=>{
let x=1
resolve(x)
})
}
a()
接着新建我们的promise4.js文件
每次new出了一个promise实例所以Promise可以是一个类然后导出它
class Promise{
constructor() {
}
}
module.exports=Promise
然后我们再来完善它,给构造器传入一个函数executor,包含成功和异常的回调。
class Promise{
constructor(executor) {
//传入一个executor执行器函数
const resolve=(value)=>{
console.log(value);
}//成功的回调
const reject=(reason)=>{
console.log(reason);
}//异常的回调
executor(resolve,reject)
//当new出promise实例时此函数立即执行
}
}
module.exports=Promise
运行下控制台成功返回了成功的回调
Promise的三种状态及then方法基本结构
接着我们看看promise的三种状态
状态的改变不可逆,一旦状态改变就不能再更改。
- pending - 进行中
- fulfilled - 成功
- rejected - 失败
新增状态
所以我们给它新增初始的状态state=‘pending’,并且在执行两个回调里先判断promise状态state是否为pending,是才往下执行。
新增then方法
then方法同样需要两个回调,在判断状态为成功的fulfilled或失败的rejected后执行回调并传入需要的值。
class Promise{
constructor(executor) {
//传入一个executor执行器函数,当new出promise实例时立即执行
this.state='pending'
this.value='undefined'
this.reason='undefined'
//初始化promise的三种状态
const resolve=(value)=>{
//对promise状态进行判断
if(this.state==='pending'){
this.state='fulfilled'
this.value=value
}
}
const reject=(reason)=>{
//对promise状态进行判断
if(this.state==='pending'){
this.state='rejected'
this.reason=reason
}
}
executor(resolve,reject)
}
then(onFulfilled,onRejected){
if(this.state==='fulfilled'){
onFulfilled(this.value)
}
if(this.state==='rejected'){
onRejected(this.reason)
}
}
}
module.exports=Promise
然后我们测试一下看看:这里打印了成功的回调res参数
const Promise = require('./promise4.js')
function a(){
return new Promise((resolve,reject)=>{
let x=1
resolve(x)
})
}
a().then(res=>{
console.log(res);
},err=>{
console.log(res+'==');
})
执行效果如下:成功返回了1
让Promise支持异步操作
我们先给promise加上异步操作试试,这里用一个定时器代替效果。
const Promise = require('./promise4.js')
function a(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
let x=1
resolve(x)
},1000)
})
}
a().then(res=>{
console.log(res);
},err=>{
console.log(res+'==');
})
运行以上代码发现控制台并无输出
这是因为在new一个promise对象后让promise状态改变的resolve和reject函数并没有执行,他需要等待一秒后才会执行,而此时却立即执行了then方法。所以此时的state还是pending状态。所以then里面的代码并不会执行。
打印出then中此时的state为pending
异步解决办法
我们这里参照发布订阅者模式,具体思路如下:在执行then方法时先进行判断,如果此时的状态是pending,就把回调函数放到一个数组里,当状态改变时forEach从数组中循环取出执行,新增两个Array类型的数组,用于存放回调函数:
接着在then中新增pending状态的判断
then(onFulfilled,onRejected){
// console.log(this.state);
if(this.state==='fulfilled'){
onFulfilled(this.value)
}
if(this.state==='rejected'){
onRejected(this.reason)
}
//新增判断如果为pending状态则将回调函数依次放入数组中且不执行!
if(this.state==='pending'){
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason)
})
}
}
接着就是当状态改变时执行我们的回调即可
运行代码现发现在1秒后控制台成功打印出了1,那么我们的promise已经是支持异步操作了。
链式调用
我们在p1后需要再次.then(需要注意的是p1它return的可能是串字符也可能是个promise)
const Promise = require('./promise4.js')
function a(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
let x=1
resolve(x)
},1000)
})
}
const p1=a().then(res=>{
console.log(res);
//return 'hahaha'
return new Promise((resolve,reject)=>{
resolve('========')
})
},err=>{
console.log(res+'==');
})
//链式操作
const p2=p1.then(res=>{
console.log(res);
})
所以then方法里需要再次return 一个promise,我们在then里新增promise并return它。
then(onFulfilled,onRejected){
// console.log(this.state);
const p2=new Promise((resolve,reject)=>{
if(this.state==='fulfilled'){
onFulfilled(this.value)
}
if(this.state==='rejected'){
onRejected(this.reason)
}
//新增判断如果为pending状态则将回调函数依次放入数组中且不执行!
if(this.state==='pending'){
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason)
})
}
})
return p2;
}
因为return的可能是一个promise,所以我们需要做次判断
新增resolvePromise函数
这里新增一个resolvePromise函数对其进行操作。
then(onFulfilled,onRejected){
// console.log(this.state);
const p2=new Promise((resolve,reject)=>{
if(this.state==='fulfilled'){
const x = onFulfilled(this.value)
//此时的x可能为字符串数字或promise等,所以我们需要再对它进行一层操作
resolvePromise(p2,x,resolve,reject)
}
if(this.state==='rejected'){
onRejected(this.reason)
}
//新增判断如果为pending状态则将回调函数依次放入数组中且不执行!
if(this.state==='pending'){
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(()=>{
onRejected(this.reason)
})
}
})
return p2;
}
由于此时的p2还没产生,这里需要一个定时器使其进入下一事件循环。
同理下面的其他状态判断也需加上同样的代码(完善后的完整代码如下)
then(onFulfilled,onRejected){
const promise2=new Promise((resolve,reject)=>{
if(this.state==='fulfilled'){
setTimeout(()=>{
const x=onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
},0)
}
if(this.state==='rejected'){
setTimeout(()=>{
const x=onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
},0)
}
if(this.state==='pending'){
this.onResolvedCallbacks.push(()=>{
setTimeout(()=>{
const x=onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
},0)
})
this.onRejectedCallbacks.push(()=>{
setTimeout(()=>{
const x=onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
},0)
})
}
})
return promise2
}
书写resolvePromise函数
const resolvePromise=(promise2,x,resolve,reject)=>{
//异常排除
if(promise2===x){
return reject(new TypeError('循环引用'))
}
//x类型排除
if(typeof x === 'function' || (typeof x === 'object' && x!== null)){
try{
const then=x.then
if(typeof then=== 'function'){//排除.then是个属性,所以此处认定他是promise
then.call(x,y=>{
resolve(y)
},r=>{
reject(r)
})
}
}catch(error){
reject(error)
}
}else{
resolve(x)
}
}
接着运行代码我们p1return的’======'则通过链式调用顺利被输出了。