我所知道的关于Promise的一些知识

152 阅读5分钟

众所周知,对于存在异步编程而产生的回调地狱问题,Promise的出现很好的解决了这样的问题。

首先,我们所要先了解的是,回调地狱是什么?

那......

什么是回调地狱?

某些异步任务想要实现按顺序执行的话,需要将回调函数层层嵌套堆叠,但是这种层层嵌套的回调函数经常会导致回调地狱问题。比如:

// 吃饭
function Eat(callback) {
    setTimeout(() => {
        callback("eat");
    }, 1000);
}

// 煮饭
funtion Cook(callback) {
    setTimeout(() => {
        callback("cook");
    }, 1000);
}

// 清洗厨具
function Clean(callback) {
    setTimeout(() => {
        callback("clean");
    }, 1000);
}

// 完成吃饭
Eat((data) => {
   console.log(data);
   Cook((data) => {
       console.log(data);
       Clean(() => {
           console.log(data);
       })
   })
})

例如以上代码中所展示的,要完成A任务,必须要相继完成多个(B、C、D...)任务,而这样实现的话,调用的方法越多,就会使得代码维护愈发困难,这种可复用性差、迭代性差的回调函数层层嵌套方式,就是所谓的回调地狱

而Promise解决回调地狱问题,并不是控制函数执行的顺序(这个是控制不了的),而是通过控制异步代码处理的顺序来完成的。

Promise中的resolvereject两个回调方法就是用来实现异步任务同步执行的。

Promise的基础认知

Promise的三种状态

Promise有三种状态,分别是:pending(进行中)、fullfilled(成功)、rejected(失败)。 resolve处理之后的结果返回的是fullfilledreject处理之后的结果返回的是rejected,而如果没有处理Promise的返回结果则会一直在pending进行中。

let p = new Promise((resolve,reject) => {
       resolve("fullfilled"); // 成功 fullfilled
       reject("rejected"); // 失败 rejected
});

Promise.then

Promise通过.then完成下一步,会返回resolve中传递的结果(成功和失败),在.then中再新建一个Promise对象的话,便可以继续执行下一步,返回下一个结果。

.then在什么情况下才能执行呢? 只有在Promise中成功调用了resolve方法或者是return数据之后,才会执行.then方法。

Promise.catch

.catch是在reject返回错误结果之后才会执行。

Promise其他方法

Promise还存在其他方法: 1、 原型方法: finally (包括catch) 2、 静态方法: raceallallSettledresolvereject...

如何用JS实现简单版的Promise呢?

首先,Promise是一个可以创建的实例对象。那我们可以通过class类虚拟建一个Promise对象

与此同时,Promise对象在创建之时,还有两个回调函数resolve 和 rejectPromise中的回调会传递结果到下一步,则需要将其结果赋值给Promise中的“结果”(PromiseResult)。还需要根据回调函数改变其状态(PromiseState)

class Promise {
    constructor(handle) {
        this['[[PromiseState]]'] = 'pending';
        this['[[PromiseResult]]'] = undefined;
        handle(this.#resolve.bind(this), this.#reject.bind(this));
    }
    
    #resolve(val) {
        this['[[PromiseState]]'] = 'fulfilled';
        this['[[PromiseResult]]'] = val;
    }
    
    #reject(err) {
        this['[[PromiseState]]'] = 'rejected';
        this['[[PromiseResult]]'] = err;
    }
}

then怎么实现呢?

.then会调用一个回调函数,该回调函数会返回resolve/reject中传递的结果(前提是:调用了resolve方法),我们可以设置一个全局的resolveFnrejectFn用来作为判断then中执行了哪些回调,并可以在resolve/reject中调用将数据传递出去。

class Promise {
    constructor(handle) {
        this['[[PromiseState]]'] = 'pending';
        this['[[PromiseResult]]'] = undefined;
        this.resolveFn = undefined;
        this.rejectFn = undefined;
        handle(this.#resolve.bind(this), this.#reject.bind(this));
    }
    
    #resolve(val) {
        this['[[PromiseState]]'] = 'fulfilled';
        this['[[PromiseResult]]'] = val;
        this.resolveFn && this.resolveFn(val);
    }
    
    #reject(err) {
        this['[[PromiseState]]'] = 'rejected';
        this['[[PromiseResult]]'] = err;
        this.rejectFn && this.rejectFn(err);
    }
    
    then(onResolved, onRejected) {
        this.resolveFn = onResolved;
        this.rejectFn = onRejected;
    }
}

然而这样实现的话会产生很多问题,咱就是说,多个then的情况的话,不可以保证,且不能正确的执行事件循环的顺序。

解决多个then、链式操作和事件循环问题

多个then的话,我们可以通过定义一个数组(resolveQueuerejectQueue)用来存储多个then中回调执行的函数,当第一个回调执行完成之后,再执行数组中下一个回调(可以通过shift将首项取出来)。

事件循环可以通过MutationObserver对象模拟执行微任务。

链式操作,我们可以通过在then返回一个新建的Promise对象,这样的话,可以不断的对其return的Promise对象进行.then

如下:

class Promise {
    constructor(handle) {
        this['[[PromiseState]]'] = 'pending';
        this['[[PromiseResult]]'] = undefined;
        // this.resolveFn = undefined;
        // this.rejectFn = undefined;
        this.resolveQueue = [];
        this.rejectQueue = [];
        handle(this.#resolve.bind(this), this.#reject.bind(this));
    }
    #resolve(val) {
        this['[[PromiseState]]'] = 'fulfilled';
        this['[[PromiseResult]]'] = val;
        // this.resolveFn && this.resolveFn(val);
        const run = () => {
            // 解决多个then的问题
            let cb;
            while(cb = this.resolveQueue.shift()) {
                // 将第一项删除出来赋值给cb
                cb && cb(val);
            }
        }
        // run(); // 不能异步
        // setTimeout(run); // 影响微任务宏任务运行
        // 宏任务微任务处理
        const observer = new MutationObserver(run);
        observer.observe(document.body, {
            attributes: true,
        });
        document.body.setAttribute('test', 'value');

    }
    #reject(err) {
        this['[[PromiseState]]'] = 'rejected';
        this['[[PromiseResult]]'] = err;
        // this.rejectFn && this.rejectFn(err);
        const run = () => {
            // 解决多个then的问题
            let cb;
            while(cb = this.rejectQueue.shift()) {
                // 将第一项删除出来赋值给cb
                cb && cb(err);
            }
        }
        // run(); // 不能异步
        // setTimeout(run); // 影响微任务宏任务运行
        // 宏任务微任务处理
        const observer = new MutationObserver(run);
        observer.observe(document.body, {
            attributes: true,
        });
        document.body.setAttribute('test', 'value');
    }

    then(onResolved, onRejected) {
        // if(this['[[PromiseState]]'] === 'fulfilled') {
        //     onResolved && onResolved(this['[[PromiseResult]]']);
        // } else {
        //     onRejected && onRejected(this['[[PromiseResult]]']);
        // }

        // 链式操作
        return new Promise((resolve, reject) => {
            let resolveFn = function(val) {
                let result = onResolved && onResolved(val);
                // 如果返还的是Promise对象 返还值处理
                if(result instanceof CPromise) {
                    result.then(res => {
                        resolve(res);
                    })
                } else {
                    resolve(result);
                }             
            }
            this.resolveQueue.push(resolveFn);

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

        // this.resolveFn = onResolved;
        // this.rejectFn = onRejected;
        // this.resolveQueue.push(onResolved);
        // this.rejectQueue.push(onRejected);                
    }
}

一些其他方法的简单实现

Promise中不仅有then,还有一些其他的方法,关于这些方法,其实都可以通过在其中新建一个Promise对象,然后通过已有方法来帮助实现。

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

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

    static reject(err) {
        return new Promise(reject => {
            reject(err);
        })
    }
    
    static allSettled(lists) {
        // 返回结果与lists的长度保持一致
        let resArr = new Array(lists.length);
        // 数目一致之后可以直接返回resArr
        let num = 0;
        // 返回一个新的Promise对象
        return new Promise(resolve => {
            // 循环遍历传入的数组
            lists.forEach((item, key) => {
                let obj = {};
                // 处理传入的实例对象中的结果,将其结果保存到resArr中,然后再将resArr作为最终结果返回
                item.then(res => {
                    obj["status"] = "fulfilled";
                    obj["value"] = res;
                    resArr[key] = obj;
                    num++;
                    if(num >= lists.length) {
                        resolve(resArr);
                    }
                }, err => {
                    obj["status"] = "rejected";
                    obj["reson"] = err;
                    resArr[key] = obj;
                    num++;
                    if(num >= lists.length) {
                        resolve(resArr);
                    }
                })
            })
        })
    }

可以使用async/await来实现Promise,是Promise的一种高级写法,更加的方便。