promise原理

361 阅读3分钟

说到promise就会想到下面几个疑问。
1、为什么会出现promise
2、它的作用是什么?
3、它的原理是什么,怎么实现的呢?

既然知道了问题点,就可以一个个的解决它


为什么会有promise?

// 一个简单的示例 执行一个动画A,执行完之后再去执行另一个动画B
setTimeout(function(){
    console.log('A');   //A动画
    setTimeout(function() {
        console.log('B'); // //B动画
    },300)
},300);
// 这里只有两个动画,如果有更多呢,就会看到一堆函数缩进

很明显,如果有更多的动画,就会使代码横向发展而非纵向发展。乱成一团,很难管理。多个异步操作形成了强耦合,只要有一个操作修改,它的上层回调和下层回调可能都会修改,这种情况称之为“回调函数地域(callback hell)”。promise的出现解决了这个问题,它允许将函数嵌套改成链式调用,使我们可以像写同步代码一样去写异步代码。

注:promise不是新的语法功能,是一种新的写法

promise的原理
promise其实就是三个状态,利用观察者模式通过特殊的手写方式注册对应状态的事件处理函数,然后改变状态,调用处理函数
特殊的方式:

  • .done 成功处理函数列表
  • .fail 失败处理函数列表
  • .then 同时注册成功与失败处理函数
  • .always 一个对象从注册到成功和失败

更新状态

  • pending(等待)
  • resolved(成功)
  • rejected(失败)

如何实现,那就来看代码把~

/*
 * state状态:PENDING、RESOLVED、REJECTED
 * .doneList    成功处理函数列表
 * .failList    失败处理函数列表
 * .done    注册成功处理函数
 * .fail    注册失败处理函数    
 * .then    同时注册成功和失败的处理函数
 * .always  一个对象从等待到成功和失败
 * .resolved    更新state状态为resolved,并执行成功处理序列
 * .rejected    更新state状态为rejected,并执行失败处理序
*/
class MyPromise{
    constructor(fn){
        this.state = "PENDING";
        this.doneList = []
        this.failList = []
        fn(this.resolve.bind(this),this.reject.bind(this)) // 确保this指向正确
    }
    done(handle){
        if(typeof handle === 'funciton'){
            this.doneList.push(handle)
        }else{
           throw new Error('缺少回调函数');
        }
        return this
    }
    fail(handle){
        if(typeof handle === 'function'){
            this.failList.push(handle)
        }else{
            throw new Error('没有回调函数')
        }
        return this
    }
    then(success,fail){
        this.done(success || function(){})
            .fail(fail || function(){})
        return this
    }
    always(handle){
        this.done(handle || function(){})
            .fail(handle || function(){})
        return this
    }
    //doneList中第一个函数的返回值是第二个函数的参数,第二个函数的返回值是第三个函数的参数。failList同理。
    resolve(...cbs){
        if(this.state === 'PENDING'){
            this.state = "RESOLVED"
            setTimeout(()=>{
               this.doneList.forEach((fn)=>{
                  sth = fn.apply(null,cbs)
               }) 
            },0)
            return this
        }
    }
    reject(...cbs){
        if(this.state === 'PENDING'){
            this.state = "REJECTED"
            setTimeout(()=>{
               this.failList.forEach((fn)=>{
                  sth = fn.apply(null,cbs)
               }) 
            },0)
            return this
        }
    }
}
new MyPromise((resolve,reject)=>{
    resolve('MyPromise --- You Are Right')
    // reject('MyPromise --- You Are Error')
}).then(
    success => {
        console.log(success)
    },
    fail => {
        console.log(fail)
    }
)

解释: 当我们调用new MyPromise(fn)时,就会立即执行第一个参数fn。上面案例中,先将then(done,fail)对应的回调函数分别加入到doneList或者failList中,always中对应的参数同时加到doneList和failList中;

当异步代码执行完毕,就调用resolve方法,此时改变promise的状态为resolved,并执行doneList里面的回调函数。如果执行失败,则调用fail方法,此时改变promise的状态为rejected,并执行failList里面的回调函数。