从零开始封装一个Promise

一、Promise是什么?

概念:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。
作用:主要通过链式调用解决地狱回调问题

二、手写封装promise及调用

1.封装promise

完整代码如下:

const PENDING = 'PENDING',
      FUILLED = 'FUILLED',
      REJECTED = 'REJECTED';
function resolvePromise(x,resolve,reject) {
    // let called = false;
    if((typeof x === 'object' &&  typeof x !== 'null') || typeof x === 'function') {
        try {
            let then = x.then;
            if(typeof then === 'function') {
                then.call(x,(y) => {
                    // if(called) return;
                    // called = true;
                    resolve(y)
                },(r) => {;
                    reject(r)
                })
            } else {
                resolve(x)
            }
        } catch(e) {
            reject(e)
        }
    } else {
        resolve(x)
    }
}
class myPromise {
    constructor(excutor) {
       this.status = PENDING;
       this.success = null;
       this.reason = null;
       this.resolveArray = [];
       this.rejectArray = [];
       const resolve = (res) => {
           if(this.status === PENDING) {
                this.status = FUILLED;
                this.success = res;
            }
           this.resolveArray.forEach(fn => fn())
       }
       const reject = (rej) => {
        if(this.status === PENDING) {
            this.status = REJECTED;
            this.reason = rej;
        }
        this.rejectArray.forEach(fn => fn())
       }
       excutor(resolve,reject)
    }
    then (onFuilled,onRejected) {
        let promise1 = new myPromise((resolve,reject) => {
            if(this.status === FUILLED) {
                setTimeout(() => {
                    try {
                        let x = onFuilled(this.success);
                        resolvePromise(x,resolve,reject)
                    } catch(e) {
                        // console.log(e,'eee')
                        onRejected(e)
                    }
                },2000)
            }
           
            if(this.status === REJECTED) {
                setTimeout(() => {
                    let x = onRejected(this.reason);
                    resolvePromise(x,resolve,reject)
                },2000)
            }

            if(this.status === PENDING) {
                this.resolveArray.push(() => {
                onFuilled(this.success)
                });
                this.rejectArray.push(() => {
                  onRejected(this.reason)
                });
            }
        });
        return promise1
    }
    catch(callback) {
      return this.then(null,callback)
    }
}
module.exports  = myPromise

2.Promise调用

代码如下(示例):

const myPromise = require ('./myPromise.js');
const p2 = new myPromise((resolve,reject) => {
  resolve('my love')
});
const p3 = p2.then((res) => {
    return new myPromise((resolve,reject) => {
        reject('122222')
    })
})
p3.then((res) => {
    console.log(res, 'res444')
}).catch((e) => {
    console.log(e,'e')
})

三. 封装思路解析

1. 基本特性

       
const p1 = new Promise((resolve,reject) => {
      resolve("success")
})
p1.then(res => {
 console.log(res,'res')
},(reason) => {
 console.log(reason,'reason')
})

(1) new Promise包裹的是一个执行器,类似于excutor,且resolve和reject中两者只能执行一个,执行完后Promise的状态由pending更改成fuifilled(成功)或rejected(失败),且状态不可更改,此方法在new的时候会立即调用,所以可以封装在class的constructor函数中。
(2)then中包裹的其实是两个函数,第一个为成功的函数,第二个为失败的函数,两者中一次也只能调用其中一个,其调用哪个是根据promise实例的状态决定,即创建promise实例是调用resolve还是reject决定
(3)then的调用是异步调用,且在原生Promise中是微任务调用,在此封装时,用宏任务(setTimeOut)代替

封装代码如下

class myPromise {
    constructor(excutor) {
       this.status = PENDING;
       this.success = null;
       this.reason = null;
       const resolve = (res) => {
           if(this.status === PENDING) {
                this.status = FUILLED;
                this.success = res;
            }
       }
       const reject = (rej) => {
        if(this.status === PENDING) {
            this.status = REJECTED;
            this.reason = rej;
        }
       }
       excutor(resolve,reject)
    }
    then (onFuilled,onRejected) {
        if(this.status === FUILLED) {
                setTimeout(() => {
                	onFuilled(this.success);
                },0)
            }
           
            if(this.status === REJECTED) {
                setTimeout(() => {
                   onRejected(this.reason);
                },0)
            }

    }
}
module.exports  = myPromise

2. 异步调用问题

const myPromise = require ('./myPromise.js');
const p2 = new myPromise((resolve,reject) => {
    setTimeout(() => {
        resolve('my love')
    },1000)
});
p2.then(res => {
    console.log(res,'resss')
},reason => {
    console.log(reason)
})
p2.then(res => {
    console.log(res,'resss')
},reason => {
    console.log(reason)
})

问题: 因为在执行then操作时,尚未执行到excutor执行器,所以在执行then时还是Pending状态,所以then中的两个方法调用失败
解决方法: 由于一个promise实例多次调用then时,其每次都会调用同一个状态的方法(resolve或者reject),这时我们可以将未来要调用的函数收集起来,然后等到真正执行resolve或者reject函数时去循环调用收集到的方法,因为方法是固定的,而变量的传参在执行resolve或reject的时候已经可以正确赋值了(发布订阅模式)

封装代码如下

 1. construcor部分
        this.resolveArray = [];
        this.rejectArray = [];
         const resolve = (res) => {
           if(this.status === PENDING) {
                this.status = FUILLED;
                this.success = res;
            }
           this.resolveArray.forEach(fn => fn())
       }
       const reject = (rej) => {
        if(this.status === PENDING) {
            this.status = REJECTED;
            this.reason = rej;
        }
        this.rejectArray.forEach(fn => fn())
       }
        
 2. then部分
        if(this.status === PENDING) {
                this.resolveArray.push(() => {
                onFuilled(this.success)
                });
                this.rejectArray.push(() => {
                  onRejected(this.reason)
                });
         }

3. then链式封装

原生特性:

(1) 之前能够链式调用的原因是因为then或者catch之后返回了一个promise实例,所以可以一直调用下去
(2) 调用then成功的场景: 返回promise成功结果或者普通的javascript代码
(3)调用then失败函数或者catch的场景:返回promise错误结果或者抛出错误

封装思路

(1)then中返回一个promise,并且收集调用结果,可能是个promise实例,也可能是其它数据类型;
(2)let x = x.then 调用,然后通过then.call的方法调用excutor方法,改变promise实例状态,传递值。

封装代码如下

  1. then部分(部分示例)
        then (onFuilled,onRejected) {
        let promise1 = new myPromise((resolve,reject) => {
            if(this.status === FUILLED) {
                setTimeout(() => {
                    try {
                        let x = onFuilled(this.success);
                        // 递归调用
                        resolvePromise(x,resolve,reject)
                    } catch(e) {
                        // console.log(e,'eee')
                        onRejected(e)
                    }
                },2000)
            }
           
            if(this.status === REJECTED) {
                setTimeout(() => {
                    let x = onRejected(this.reason);
                    resolvePromise(x,resolve,reject)
                },2000)
            }

            if(this.status === PENDING) {
                this.resolveArray.push(() => {
                onFuilled(this.success)
                });
                this.rejectArray.push(() => {
                  onRejected(this.reason)
                });
            }
        });
        return promise1
    }
  2.  resolvePromise方法
  	function resolvePromise(x,resolve,reject) {
    // let called = false;
    if((typeof x === 'object' &&  typeof x !== 'null') || typeof x === 'function') {
        try {
            let then = x.then;
            if(typeof then === 'function') {
                then.call(x,(y) => {
                    // if(called) return;
                    // called = true;
                    resolve(y)
                },(r) => {;
                    reject(r)
                })
            } else {
                resolve(x)
            }
        } catch(e) {
            reject(e)
        }
    } else {
        resolve(x)
    }
}

4. catch

catch其实是then的二次封装

封装代码

catch(callback) {
   return this.then(null,callback)
}

和then方法第二个函数的区别

(1)就近调用
(2) 如果catch不调用,将会阻塞catch后的调用

5. 递归调用

 setTimeout(() => {
                    try {
                        let x = onFuilled(this.success);
                        // 递归调用
                        resolvePromise(x,resolve,reject)
                    } catch(e) {
                        // console.log(e,'eee')
                        onRejected(e)
                    }
                },2000)

总结

此次封装,主要针对promise的基本调用和异步调用,链式调用进行封装等.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值