Promise原理剖析

115 阅读7分钟

一、Promise是什么?

Promise 是异步编程的一种解决方案,Promise 它为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。

1.Promise的基本结构

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("FULFILLED")
    })
})

Promise构造函数必须接受一个参数,并且参数是一个函数,我们命名为handle,handle函数又包含两个参数resolve和reject,它们两个是函数。 第一步,我们先定义名为KPromise的class,它接受一个handle函数作为参数

class KPromise {
    constructor (handle) {
    }
}

接下来,我们再看promise状态值的处理

2.Promise状态和值

Promise 对象存在以下三种状态:

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)

状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。 Promise的值是指状态改变时传递给回调函数的值。

上文中handle函数包含 resolve 和 reject 两个参数,它们是两个函数,可以用于改变 Promise 的状态和传入 Promise 的值

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("FULFILLED")
    })
})

这里 resolve 传入的 "FULFILLED" 就是 Promise 的值

resolvereject

  • resolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)
  • reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)
  • resolvereject 都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)`的值

了解了Promise的状态和值,接下来我们为KPromise添加状态属性和值,并添加改变状态的执行逻辑

class KPromise {
    constructor (handle) {
        if (!isFunction(handle)) {
            throw new Error('MyPromise must accept a function as a parameter')
        }
        this['[[PromiseState]]'] = "pending"
        this['[[PromiseResult]]'] = "undefined"
        // 执行handle
        try { 
            handle(this.#resolve.bind(this), this.#reject.bind(this))
        } catch (err) { 
            this._reject(err) 
        }
    }
    #resolve(val) {
        this['[[PromiseState]]'] = "fulfilled"
        this['[[PromiseResult]]'] = val
    }
    #reject(err) {
        this['[[PromiseState]]'] = "rejected"
        this['[[PromiseResult]]'] = err
    }
}

这样就实现了 Promise 状态和值的改变。下面说一说 Promise 的核心: then 方法

3.Promise的then方法

Promise 对象的 then 方法接受两个参数,并且为函数。

then参数的调用

  • 当 promise 状态变为成功时必须被调用,其第一个参数为 promise 成功状态传入的值( resolve 执行时传入的值)
  • 当 promise 状态变为失败时必须被调用,其第一个参数为 promise 失败状态传入的值( reject 执行时传入的值)
let promise = new KPromise((resolve, reject) => {
    setTimeout(() => {
        resolve("FULFILLED")
    }, 1000)
})
// 第一种:then参数的调用
promise.then(res => {
     console.log(res);  //FULFILLED
}, err => {
    
})

实现方式:

class KPromise {
    constructor (handle) {
        if (!isFunction(handle)) {
            throw new Error('MyPromise must accept a function as a parameter')
        }
        this['[[PromiseState]]'] = "pending"
        this['[[PromiseResult]]'] = "undefined"
        
        // 解决了异步处理的问题
        // this.resolveFn = undefined;
        // this.rejectFn = undefined;
        
        // 执行handle
        try { 
            handle(this.#resolve.bind(this), this.#reject.bind(this))
        } catch (err) { 
            this._reject(err) 
        }
    }
    #resolve(val) {
        this['[[PromiseState]]'] = "fulfilled"
        this['[[PromiseResult]]'] = val
        // 解决了异步处理的问题
        // this.resolveFn(val);
    }
    #reject(err) {
        this['[[PromiseState]]'] = "rejected"
        this['[[PromiseResult]]'] = err
        // this.rejectFn(val);
        / run();
        // setTimeout(run)
        
        // 创建一个观察器实例并传入回调函数
        const observer = new MutationObserver(run);
        observer.observe(document.body, {
          attributes: true
        });
        document.body.setAttribute("a", "b");
    }
    then(onResolved, onRejected) {
        // 存在异步问题
        /**
        if(this['[[PromiseState]]'] = "fulfilled") {
            onResolved(this['[[PromiseResult]]'])
        }else {
            onRejected(this['[[PromiseResult]]'])
        }*/
        
        
        //解决了异步处理的问题
        /**
        this.resolveFn = onResolved;
        this.rejectFn = onRejected;
        */
    }
}

then多次调用

Promise 对象的 then 方法允许多次调用。

// 第二种:then多次调用,多个then的问题 数组存储 依次执行。
promise.then(res=>{
    console.log("1111",res);  //1111 FULFILLED
})
promise.then(res=>{
    console.log("2222",res);  //2222 FULFILLED
})

多个then的实现方式

class KPromise {
    constructor (handle) {
        if (!isFunction(handle)) {
            throw new Error('MyPromise must accept a function as a parameter')
        }
        this['[[PromiseState]]'] = "pending"
        this['[[PromiseResult]]'] = "undefined"
        
        / 解决了异步处理的问题
        // this.resolveFn = undefined;
        // this.rejectFn = undefined;
        
        // 执行handle
        try { 
            handle(this.#resolve.bind(this), this.#reject.bind(this))
        } catch (err) { 
            this._reject(err) 
        }
    }
    #resolve(val) {
        this['[[PromiseState]]'] = "fulfilled"
        this['[[PromiseResult]]'] = val
        // 解决了异步处理的问题
        // this.resolveFn(val);
        
        // 解决多个then的问题
        let run = () => {
            let cb;
          while (cb = this.resolveQueue.shift()) {
            cb && cb(val);
          }
        }
        //会存在着同步的问题
        // run();
        //解决同步的问题,让执行放在异步队列中,存在宏任务问题
        // setTimeout(run)
    }
    #reject(err) {
        this['[[PromiseState]]'] = "rejected"
        this['[[PromiseResult]]'] = err
        
        // this.rejectFn(val);
        let run = () => {
            let cb;
            // [fn1,fn2....]
            while (cb = this.rejectQueue.shift()) {
                cb && cb(err);
            }
        }
        // run();
        // setTimeout(run);
    }
    then(onResolved, onRejected) {
        // 存在异步问题
        /**
        if(this['[[PromiseState]]'] = "fulfilled") {
            onResolved(this['[[PromiseResult]]'])
        }else {
            onRejected(this['[[PromiseResult]]'])
        }*/
        
        
        //解决了异步处理的问题
        /**
        this.resolveFn = onResolved;
        this.rejectFn = onRejected;
        */
  
        //解决多个then的问题
        /**
        this.resolveQueue.push(onResolved);
        this.rejectQueue.push(onRejected);
        */
    }
}

promise微任务问题

但以上的写法存在一定的问题,也就是promise(then)是个微任务

  
// 第三种:(执行顺序问题)微任务宏任务问题,同步-异步-微任务-宏任务
// 222->333->555->111-->444
setTimeout(() => {
    console.log(111);
});
console.log(222);
let p = new KPromise((resolve, reject) => {
    console.log(333);
    resolve("success");
    // reject("err")
})

// 先调用resolve 在调用then
p.then(res => {
    console.log(444,res);
},err=>{
    console.log(err);
})
console.log(555);

KPromise改成微任务的实现方式

class KPromise {
    constructor (handle) {
        if (!isFunction(handle)) {
            throw new Error('MyPromise must accept a function as a parameter')
        }
        this['[[PromiseState]]'] = "pending"
        this['[[PromiseResult]]'] = "undefined"
        
        / 解决了异步处理的问题
        // this.resolveFn = undefined;
        // this.rejectFn = undefined;
        
        // 执行handle
        try { 
            handle(this.#resolve.bind(this), this.#reject.bind(this))
        } catch (err) { 
            this._reject(err) 
        }
    }
    #resolve(val) {
        this['[[PromiseState]]'] = "fulfilled"
        this['[[PromiseResult]]'] = val
        // 解决了异步处理的问题
        // this.resolveFn(val);
        
        // 解决多个then的问题
        let run = () => {
            let cb;
          while (cb = this.resolveQueue.shift()) {
            cb && cb(val);
          }
        }
        //会存在着同步的问题
        // run();
        //解决同步的问题,让执行放在异步队列中,存在宏任务问题
        // setTimeout(run)
        
        // 解决宏任务问题
        // 创建一个观察器实例并传入回调函数
        const observer = new MutationObserver(run);
        observer.observe(document.body, {
          attributes: true
        });
        document.body.setAttribute("a", "b")
        
    }
    #reject(err) {
        this['[[PromiseState]]'] = "rejected"
        this['[[PromiseResult]]'] = err
        
        // this.rejectFn(val);
        let run = () => {
            let cb;
            // [fn1,fn2....]
            while (cb = this.rejectQueue.shift()) {
                cb && cb(err);
            }
        }
        // run();
        // setTimeout(run);
        
        // 创建一个观察器实例并传入回调函数
        const observer = new MutationObserver(run);
        observer.observe(document.body, {
          attributes: true
        });
        document.body.setAttribute("a", "b");
    }
    then(onResolved, onRejected) {
        // 存在异步问题
        /**
        if(this['[[PromiseState]]'] = "fulfilled") {
            onResolved(this['[[PromiseResult]]'])
        }else {
            onRejected(this['[[PromiseResult]]'])
        }*/
        
        
        //解决了异步处理的问题
        /**
        this.resolveFn = onResolved;
        this.rejectFn = onRejected;
        */
  
        //解决多个then的问题
        /**
        this.resolveQueue.push(onResolved);
        this.rejectQueue.push(onRejected);
        */
    }
}

then的链式调用

then 方法要链式调用那么就需要返回一个 Promise 对象,then 方法里面 return 一个返回值作为下一个 then 方法的参数,如果是 return 一个 Promise 对象,那么就需要判断它的状态

let p = new KPromise(resolve => {
    resolve("success");
})
p.then(res => {
    console.log(res);
    //return "value";
    return new KPromise(resolve=>{
        resolve("返还的值");
    })
}).then(res => {
    console.log(2, res); //2 返还的值
})

链式调用具体的实现方式

class KPromise {
    constructor (handle) {
        if (!isFunction(handle)) {
            throw new Error('MyPromise must accept a function as a parameter')
        }
        this['[[PromiseState]]'] = "pending"
        this['[[PromiseResult]]'] = "undefined"
        
        / 解决了异步处理的问题
        // this.resolveFn = undefined;
        // this.rejectFn = undefined;
        
        // 执行handle
        try { 
            handle(this.#resolve.bind(this), this.#reject.bind(this))
        } catch (err) { 
            this._reject(err) 
        }
    }
    #resolve(val) {
        this['[[PromiseState]]'] = "fulfilled"
        this['[[PromiseResult]]'] = val
        // 解决了异步处理的问题
        // this.resolveFn(val);
        
        // 解决多个then的问题
        let run = () => {
            let cb;
          while (cb = this.resolveQueue.shift()) {
            cb && cb(val);
          }
        }
        //会存在着同步的问题
        // run();
        //解决同步的问题,让执行放在异步队列中,存在宏任务问题
        // setTimeout(run)
        
        // 解决宏任务问题
        // 创建一个观察器实例并传入回调函数
        const observer = new MutationObserver(run);
        observer.observe(document.body, {
          attributes: true
        });
        document.body.setAttribute("a", "b")
        
    }
    #reject(err) {
        this['[[PromiseState]]'] = "rejected"
        this['[[PromiseResult]]'] = err
        
        // this.rejectFn(val);
        
        / run();
        // setTimeout(run)
        
        // 创建一个观察器实例并传入回调函数
        const observer = new MutationObserver(run);
        observer.observe(document.body, {
          attributes: true
        });
        document.body.setAttribute("a", "b");
    }
    then(onResolved, onRejected) {
        // 存在异步问题
        /**
        if(this['[[PromiseState]]'] = "fulfilled") {
            onResolved(this['[[PromiseResult]]'])
        }else {
            onRejected(this['[[PromiseResult]]'])
        }*/
        
        
        //解决了异步处理的问题
        /**
        this.resolveFn = onResolved;
        this.rejectFn = onRejected;
        */
  
        //解决多个then的问题
        /**
        this.resolveQueue.push(onResolved);
        this.rejectQueue.push(onRejected);
        */
        
        //链式调用
        return new KPromise((resolve, reject) => {
         // let val = onResolved && onResolved();
            let resolveFn = function (val) {
                let reslut = onResolved && onResolved(val);
                // 返还的KPromise对象
                if (reslut instanceof KPromise) {
                    // reslut.then(res=>{
                    //     resolve(res);
                    // })
                    reslut.then(resolve);
                } else {
                    resolve(reslut);
                }
            }
         }
         this.resolveQueue.push(resolveFn);

         let rejectFn = function (err) {
             onRejected && onRejected(err);
             reject(err);
         }
         this.rejectQueue.push(rejectFn);
        })
    }
}

promise的catch

let p = new Promise((resolve, reject) => {
    reject("err");
})

p.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err); //err
})

实现方式

catch(fn) {
     return this.then(undefined, fn);
}

promise的resolve和reject

Promise.resolve(2222).then(res => {
    console.log(res); //2222
})
Promise.reject(new Error('fail')).then(res => {
  
},err => {
    console.log(err)
})

实现方式

static resolve(val) {
     return new KPromise(resolve => {
      resolve(val);
     })
}

static reject(err) {
     return new KPromise((resolve, reject) => {
          reject(err);
     })
}

promise的race方法

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("success1");
        reject("err1");
    }, 2000);
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("success2");
        //reject("err2");
    }, 1000);
})
KPromise.race([p1,p2]).then(res=>{
    console.log(res); //success2
},err=>{
    console.log(err);
});

实现方式:

static race(lists) {
     let isExe = false;
     return new KPromise((resolve, reject) => {
          lists.forEach(item => {
               item.then(res => {
                    if (!isExe) {
                         resolve(res);
                         isExe = true;
                    }
               }, err => {
                    if (!isExe) {
                         reject(err);
                         isExe = true;
                    }
               })
          })
     })
}

promise的allSettled

static allSettled(lists) {
 // let resArr = [1,2];
 let resArr = new Array(lists.length);
 let num = 0;
 return new KPromise(reslove => {
  lists.forEach((item, key) => {
   let obj = {};
   item.then(res => {
    obj['status'] = "fulfilled";
    obj['value'] = res;
    resArr[key] = obj;
    // console.log(1,resArr);
    num++;
    if (num >= lists.length) {
     reslove(resArr);
    }
   }, err => {
    obj['status'] = "rejected";
    obj['reson'] = err;
    resArr[key] = obj;
    num++;
    // console.log(2,resArr);
    if (num >= lists.length) {
     reslove(resArr);
    }
   })
  })
 })
}

promise的all

static all(lists){
    let resArr = [];
    let num = 0;
    return new KPromise(resolve=>{
        lists.forEach(item=>{
            item.then(res=>{
                num++;
                resArr.push(res);
                if(resArr.length===lists.length){
                    resolve(resArr);
                }
            })
        })
    })
}