深入解析PromiseA+设计原理

264 阅读4分钟

各位前端的小伙伴们大家好,今天我就着Promise的设计,发表一些更深入的理解。

就我个人而言,对Promise的理解有三个阶段。第一阶段,这个玩意就是用来搞异步的嘛,每次在then中取数据就好了;第二阶段,对Promise有了更深入的了解,Promise是微任务的一种体现,主线程执行完之后,就会轮询微任务队列进行任务执行。

从第一阶段和第二阶段,个人理解的对比就能很快看出来,第二阶段我对前端的理解更加宏观了,其实哈是看了一篇极客时间(浏览器原理与实践)的专刊,学到了一些关于浏览器的知识,才有了更全面的看法。

第三阶段,就是发现Promise的设计原理,跟我们前端用到最多的设计模式———发布订阅模式,是息息相关的。听我慢慢道来吧,下面我会通过一些PromiseA+规范的代码,来带你体会其优雅的设计模式。

(1)发布订阅

首先,我们先来明确一下,什么是发布订阅模式?其实很简单,主要的实现就是利用了cache,把函数体注册到cache中,如果达到某一个条件,就执行cache中的所有函数体。

// 实现一个最简单的发布订阅,当浏览器大小变化的时候,进行发布
const EventEmitter = {
    const _events = [],
    install(fn) {
        this._events.push(fn);
    },
    emit() {
        this._events.forEach(fn => fn());
    }
}
// 事件1
const fn1 = () => { console.log('事件1') }
EventEmitter.install(fn1)
const fn2 = () => { console.log('事件2') }
EventEmitter.install(fn2)

// 当滚动的时候触发
window.onresize = function () {
    EventEmitter.emit(); // 事件1 事件2
}

现在对这种设计模式有了简单的认识,或者说是回顾了一下这个知识点~

然后大家接着跟我往下走,相信大家带着这种设计模式的思想,读完整篇文章,肯定会有新的认识,下次哪个面试官让你手写Promise再也不用担心了!

(2)实现Promise Constructor 和 Promise.then

  • 1.首先Promise有三种状态Pending、Fulfilled和Rejected。
  • 2.并且状态一变就不能在悔改。
  • 3.new Promise,会直接调用回调函数。

这个一版本主要实现了一些基础的架构,this.value用来保存成功回调的返回值,this.reason用来保存失败回调的返回值。then中的两个if语句,只能对同步的代码进行访问,例子一then中两个if的逻辑都没有执行到,因为此时的this.status的值是PENDING,所以我们再进行一些优化,来处理异步的逻辑。

// 先声明三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
     constructor(excutor) {
         // 成功回调的值
         this.value = undefined;
         // 失败回调的值
         this.reason = undefined;
         this.status = PENDING;
         
         const resolve = (value) => {
             this.value = value;
             this.status = FULFILLED;
         }
         const reject = (reason) => {
             this.reason = value;
             this.status = REJECTED;
         }
         excutor(resolve, reject);
         
     }
     // 其实then方法就是发布订阅的订阅
     // 把onFulfilled onRejected方法进行注册
     then(onFulfilled, onRejected) {
         // 如果成功了
         if(this.status === FULFILLED) {
             onFulfilled(this.value);
         }
         // 如果失败了
         if(this.status === REJECTED) {
             onRejected(this.reason);
         }
     }
}

例子一

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(123);
    }, 1000)
}).then(res => { console.log(res) }) // 这里不会被打印

(3)能够处理异步代码

处理异步的逻辑,就涉及到了发布订阅模式。then中,如果this.status是PENDING状态,则存在onResolvedCallbacks和onRejectedCallbacks中,当逻辑中resolve了,则说明异步函数已经得到了结果,此时就会执行onResolvedCallbacks和onRejectedCallbacks中的函数。

所以,then方法可以理解成事件订阅,把onResolvedCallbacks和onRejectedCallbacks进行订阅;然后,当resolve或者reject的时候,将进行事件发布,执行所有订阅的事情,并且把this.vulue或者this.reason进行传参。

// 先声明三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
     constructor(excutor) {
         // 成功回调的值
         this.value = undefined;
         // 失败回调的值
         this.reason = undefined;
         this.status = PENDING;
         
         // 新增
         this.onResolvedCallbacks = [];
         this.onRejectedCallbacks = [];
         
         // 新增
         const resolve = (value) => {
             if (this.status === PENDING) {
                 this.value = value;
                 this.status = FULFILLED;
                 this.onResolvedCallbacks.forEach(cb => cb())
             }
         }
         // 新增
         const reject = (reason) => {
             if (this.status === PENDING) {
                 this.reason = value;
                 this.status = REJECTED;
                 this.onRejectedCallbacks.forEach(cb => cb())
             }
         }
         
         excutor(resolve, reject);
         
     }
     // 其实then方法就是发布订阅的订阅
     // 把onFulfilled onRejected方法进行注册
     then(onFulfilled, onRejected) {
         // 如果成功了
         if(this.status === FULFILLED) {
             onFulfilled(this.value);
         }
         // 如果失败了
         if(this.status === REJECTED) {
             onRejected(this.reason);
         }
         // 新增
         if (this.status === PENDING) {
            this.onResolvedCallbacks.push(() => {
                onFulfilled(this.value)
            });
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason)
            }); 
        }
     }
}

(4) 实现Promise的链式调用

Promise是支持链式调用的,是通过then方法中回调函数的返回值(需要注意return的有可能是promise或者非promise,需要特殊处理一下),作为下一个then方法的回调参数,可以看一下”使用演示“。

所以,实现的大体思路有三点:

  1. then方法返回一个new Promise
  2. onResolvedCallbacks和onRejectedCallbacks中的返回值进行resolve和reject
  3. 对resolve的值进行特别处理,因为有可能是promise或者非promise

使用演示:

new Promise((resolve, reject) => {
    setTimeout(() => {
        resovle(100)
    }, 1000)
}).then(res => {
    return res + 100
}).then(res2 => {
    console.log(res2) // 200
})

代码实现:

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
     constructor(excutor) {
         this.value = undefined;
         this.reason = undefined;
         this.status = PENDING;
         
         this.onResolvedCallbacks = [];
         this.onRejectedCallbacks = [];
         
         const resolve = (value) => {
             if (this.status === PENDING) {
                 this.value = value;
                 this.status = FULFILLED;
                 this.onResolvedCallbacks.forEach(cb => cb())
             }
         }
         const reject = (reason) => {
             if (this.status === PENDING) {
                 this.reason = value;
                 this.status = REJECTED;
                 this.onRejectedCallbacks.forEach(cb => cb())
             }
         }
         
         excutor(resolve, reject);
         
     }
     then(onFulfilled, onRejected) {
         if(this.status === FULFILLED) {
             // 新增
             setTimeout(() => {
                try {
                    const x = onFulfilled(this.value)
                    resolvePromise(x, promise2, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            }, 0);
         }
         if(this.status === REJECTED) {
             // 新增
             setTimeout(() => {
                try {
                    const x = onRejected(this.reason)
                    resolvePromise(x, promise2, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            }, 0);
         }
         if (this.status === PENDING) {
            this.onResolvedCallbacks.push(() => {
                // 新增
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value)
                        resolvePromise(x, promise2, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                }, 0);
            });
            this.onRejectedCallbacks.push(() => {
                // 新增
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason)
                        resolvePromise(x, promise2, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }); 
        }
        
     }
}

持续更新中...