Appearance
发布订阅的形成
本节目标:理解“发布订阅的形成”的核心思路,并能把它用于实际开发或面试表达。
说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 使用class来实现发布订阅,封装性更好,代码更简洁,对传统程序员更友好;
当使用new Promsie创造实例时,每个实例就在内部定义了几个变量用来保存:
状态成功值和失败原因成功回调函数失败回调函数用于触发事件,从而将成功回调函数依次执行的resolve方法和将失败回调函数依次执行的reject方法;我们将成功的值和失败的原因会传入回调函数;从而执行状态值变化后的一些逻辑,相当于异步代码执行完后,再将执行结果传入后面的函数,从而实现了异步编程的处理;这里,这个方法是内部方法,不是实例的本身的属性;
异步代码我们通过executor函数传入到了类的构造器中;在应用的时候,我们就是将会阻塞的代码也就是异步代码放入executor函数中;在executor方法会传入我们用来触发成功的resolve和失败回调reject两个内部方法;当我们exector中的异步代码成功时,我们就调用触发成功的resolve方法;如果异步代码失败的话,我们就触发失败的reject方法;然后,我们通过then方法来添加成功回调和失败回调;
promise实例的实例方法then方法,用来添加成功回调和失败回调;const PENDING = 'PENDING';
const FULFILLED = 'FUFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(excutor) {
// 保存状态
this.state = PENDING;
// 保存成功值
this.value = undefined;
// 保存失败值
this.reason = undefined;
// 保存状态成功后要执行的回调函数
this.resolveCallbacks = [];
// 保存状态失败后要执行的回调函数
this.rejectedCallbacks = [];
// 触发成功回调的方法
const resolve = (value) => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.resolveCallbacks.forEacth(fn => fn());
}
}
// 触发失败回调的方法
const reject = (reason) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
this.rejectedCallbacks.forEacth(fn => fn());
}
}
// 执行用户定义的控制状态成功或失败的函数
try {
excutor(resolve, resolve)
} catch (e) {
reject(e);
}
}
}then方法添加回调函数
class Promise {
constructor(excutor) {
// 保存状态
this.state = PENDING;
// 保存成功值
this.value = undefined;
// 保存失败值
this.reason = undefined;
// 保存状态成功后要执行的回调函数
const resolveCallbacks = [];
// 保存状态失败后要执行的回调函数
const rejectedCallbacks = [];
// 触发成功回调的方法
const resolve = (value) => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.resolveCallbacks.forEacth(fn => fn());
}
}
// 触发失败回调的方法
const reject = (reason) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
this.rejectedCallbacks.forEacth(fn => fn());
}
}
// 执行用户定义的控制状态成功或失败的函数
try {
excutor(resolve, resolve)
} catch (e) {
reject(e);
}
}
==then====(====onResolve====,== ==onReject====)== =={==
==//== ==我们添加回调时,如果异步代码执行完了,状态已经凝固,成功值生成,我们直接执行成功回调==
==if== ==(====this====.====state== ======= ==FULFILLED====)== =={==
==onResolve====(====this====.====value====)==
==}==
==//== ==我们添加回调时,如果异步代码执行完,状态已经凝固,失败值生成,我们直接执行成功回调==
==if== ==(====this====.====state== ======= ==REJECTED====)== =={==
==onReject====(====this====.====reason====)==
==}==
==//== ==如果异步代码还没有执行完,状态未凝固,我们把成功和失败的回调存入到类的成功回调和失败回调中==
==if== ==(====this====.====state== ======= ==PENDING====)== =={==
==this====.====resolveCallbacks====.====push====(()== ===>== =={==
==//== ==切片,可以加入一些其它逻辑==
==onResolve====(====this====.====value====)==
==})==
==this====.====rejectedCallbacks====.====push====(()== ===>== =={==
==onReject====(====this====.====reason====)==
==})==
==}==
==}==
}测试:
const p = new Promise((resolve, reject) => {
console.log('start')
resolve(1);
console.log('end')
})
p.then((v) => {
console.log(v)
})
结果解析,执行器函数会立即执行,这里,我们的成功和失败回调数组中还没有回调函数,所以,执行resolve(1)时,没有任何回调执行; 所以,依次打印出start,end; 接着,我们使用then添加回调函数;这时,我们会判断promsie的状态,如果异步代码已经执行完了,状态已经凝固,异步值已经生成,我们就直接执行成功或失败回调函数,并不会将回调函数放入到promise实例的成功和失败回调数组中去;
测试
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
console.log('start')
resolve(1);
console.log('end')
}, 1000);
})
p.then((v) => {
console.log(v)
})
打印结果:解析:执行器函数立即执行;将settimeout回调过1秒后放入事件队列;使用then添加回调,每次调用then时,如果promsie的异步代码已经执行出了结果,状态已经不是pending状态,我们就将成功的回调和失败的回调立即执行;
如果promise还是pending状态,意味着异步代码未完成,我们就将成功和操作性回调分别放入到promise实例的成功回调和失败回调数组中,等异步代码执行完毕,我们会调用resolve或reject来触发回调函数;
所以,在约1秒后,我们执行了start;然后,我们resolve(1),这代表异步代码完成,状态凝固,我们调用依次执行成功回调中函数,这时,打印出了1;
resolve或reject后,代码还会执行,打印了end;