各位前端的小伙伴们大家好,今天我就着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方法的回调参数,可以看一下”使用演示“。
所以,实现的大体思路有三点:
- then方法返回一个new Promise
- onResolvedCallbacks和onRejectedCallbacks中的返回值进行resolve和reject
- 对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);
});
}
}
}
持续更新中...