随心coding—写个promise
前言
手写promise是2022年末我找实习时被问到过的问题,当时水平还差的有点远,面试后也尝试学习过,但是相当吃力,问题留存至今,今天该了结一下了。
先说一下,下面的编码我没有参考任何其他大佬的代码,意思是可能我实现的比较不规范,功能也比较有限,只是向着自己预期的效果拍脑袋去写代码,肯定不是什么A+规范级别的,但说实话我觉着这种面试题不就是看我们的一个思路与动手能力么,如果感兴趣可以看看。
三个目标是从简到繁复现的我的编码思路,有任何问题欢迎大家批评指正!
目标一、实现同步的链式调用
目标测试用例:
// 同步代码链式调用
const test3 = new myPromise((resolve) => {
resolve(1);
})
.then((res) => {
console.log(res); // 输出1
return 2 * res;
})
.then((res) => {
console.log(res); // 输出2
});
编码实现(思路写在注释里):
class myPromise {
constructor(executor) {
// 一个promise实例就维护两个属性:一个value表示成功或者失败的值;status不用多说,pending || fullfilled || rejected
this.value = undefined;
this.status = 'pending';
// 构造函数的逻辑就是执行用户传入的executor函数,并且提供resolve和reject方法。(这些都比较基本了,照顾新人还是简单说一下
executor(this.resolve.bind(this), this.reject.bind(this));
}
// resolve要做的事情就一个:修改value和status
resolve(value) {
this.value = value;
this.status = 'fullfilled';
}
// reject与resolve同理
reject(value) {
this.value = value;
this.status = 'rejected';
}
// 为支持链式调用then方法返回一个promise实例,并根据status选择执行成功或是失败的回调即可
then(onFullfilledCallback, onRejectedCallback) {
const promise = new myPromise((resolve, reject) => {
if(this.status === 'fullfilled') {
const result = onFullfilledCallback(this.value);
// 原生Promise中then方法返回的promise实例是非pending状态的,一般value值为成功回调的返回值
resolve(result);
}
if(this.status === 'rejected') {
const result = onRejectedCallback(this.value);
reject(result);
}
})
return promise;
}
}
目标二、支持调用链中存在异步代码
(也就是延迟resolve
也能正常触发链式回调)
目标测试用例:
// 支持调用链中存在异步代码
const test = new myPromise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
console.log(res);
return 2 * res;
})
.then((res) => {
console.log(res);
});
思路分析:
熟悉js事件循环机制的话大家肯定知道上面代码的执行顺序,其实是两个then
都执行完了才会执行setTimeout
,所以当执行某个promise
实例的then
方法时这个promise
其实还是pending
状态的,说白了就是then
先执行,resolve
后执行。
所以思路就比较清晰了,我们在then
中增加对pending
状态的处理,处理逻辑就是先把then
方法指定的回调存储起来,然后等到resolve
执行时将存储的回调执行。
编码实现:
class myPromise {
constructor(executor) {
this.value = undefined;
this.status = 'pending';
// 增加两个任务队列用来存储promise实例处于pending状态下then方法指定的回调
+ this.onFullfilledCallbacks = [];
+ this.onRejectedCallbacks = [];
executor(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
this.value = value;
this.status = 'fullfilled';
// resolve时触发存储的回调函数
+ while(this.onFullfilledCallbacks.length !== 0) {
+ this.onFullfilledCallbacks.shift()(this.value);
+ }
}
reject(value) {
this.value = value;
this.status = 'rejected';
// 同理resolve,触发回调队列里的方法
+ while(this.onRejectedCallbacks.length !== 0) {
+ this.onRejectedCallbacks.shift()(this.value);
+ }
}
then(onFullfilledCallback, onRejectedCallback) {
const promise = new myPromise((resolve, reject) => {
if(this.status === 'fullfilled') {
const result = onFullfilledCallback(this.value);
resolve(result);
}
if(this.status === 'rejected') {
const result = onRejectedCallback(this.value);
reject(result);
}
// 将pending状态下遇到的回调函数存储起来
+ if(this.status === 'pending') {
+ this.onFullfilledCallbacks.push(onFullfilledCallback);
+ this.onRejectedCallbacks.push(onRejectedCallback);
+ }
})
return promise;
}
}
问题分析:
此时执行测试用例会发现只输出了1,第二个then方法的逻辑并没有被执行。
原因不难分析,因为第一个then
方法返回的promise
状态一直都是pending
从未改变,所以我们修复这个问题的思路就是让then
方法返回的promise
状态发生改变。
何时发生改变? 这里我们需要结合测试用例详细分析一下,new
返回的实例记作p1,第一个和第二个then
返回的实例分别记作p2和p3(其实p3没用,因为后面没有链式调用了)
// 支持调用链中存在异步代码
const test = new myPromise((resolve) => { // 记作p1
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => { // p2
console.log(res);
return 2 * res;
})
.then((res) => { // p3
console.log(res);
});
resolve
执行,从而触发了p1的任务队列的执行(通过第一个then收集的回调),按理讲p1状态变了,p2也要resolve
,但是p2的状态并没有改变,所以导致p2的任务队列没有执行。
解决思路就是对then
放入队列中的方法进行“增强”,就是增加修改返回的promise实例状态的逻辑
编码实现:
class myPromise {
constructor(executor) {
this.value = undefined;
this.status = 'pending';
this.onFullfilledCallbacks = [];
this.onRejectedCallbacks = [];
executor(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
this.value = value;
this.status = 'fullfilled';
while(this.onFullfilledCallbacks.length !== 0) {
+ this.onFullfilledCallbacks.shift()(); // 这里就没必要传this.value了(放到"增强"函数中了)
}
}
reject(value) {
this.value = value;
this.status = 'rejected';
while(this.onRejectedCallbacks.length !== 0) {
this.onRejectedCallbacks.shift()();
}
}
then(onFullfilledCallback, onRejectedCallback) {
const promise = new myPromise((resolve, reject) => {
if(this.status === 'fullfilled') {
const result = onFullfilledCallback(this.value);
resolve(result);
}
if(this.status === 'rejected') {
const result = onRejectedCallback(this.value);
reject(result);
}
// 对回调函数进行“增强”
+ if(this.status === 'pending') {
+ this.onFullfilledCallbacks.push(() => {
+ const result = onFullfilledCallback(this.value);
+ promise.resolve(result); // 执行完回调逻辑后同时改变promise的状态
+ });
+ this.onRejectedCallbacks.push(() => {
+ const result = onRejectedCallback(this.value);
+ promise.reject(result);
+ });
+ }
})
return promise;
}
}
控制台成功输出1、2
目标三、支持then中显示返回promise来作为then的返回值
目标测试用例:
最后正确输出this,输出第二个then中返回的promise
// 支持then中返回promise
const test2 = new myPromise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
return new myPromise((resolve) => {
resolve(2 * res);
})
})
.then((res) => {
console.log("@@@", this);
});
思路分析:
这可太简单了,说白了就是修改then
的返回值呗,在then中所有回调函数执行后判断返回值是不是promise,如果是就直接提前返回这个promise即可
代码实现:
class myPromise {
constructor(executor) {
this.value = undefined;
this.status = 'pending';
this.onFullfilledCallbacks = [];
this.onRejectedCallbacks = [];
executor(this.resolve.bind(this), this.reject.bind(this));
}
resolve(value) {
this.value = value;
this.status = 'fullfilled';
while(this.onFullfilledCallbacks.length !== 0) {
this.onFullfilledCallbacks.shift()();
}
}
reject(value) {
this.value = value;
this.status = 'rejected';
}
then(onFullfilledCallback, onRejectedCallback) {
const promise = new myPromise((resolve, reject) => {
if(this.status === 'fullfilled') {
const result = onFullfilledCallback(this.value);
resolve(result);
+ if(result instanceof myPromise) {
+ return result;
+ }
}
if(this.status === 'rejected') {
const result = onRejectedCallback(this.value);
reject(result);
+ if(result instanceof myPromise) {
+ return result;
+ }
}
if(this.status === 'pending') {
this.onFullfilledCallbacks.push(() => {
const result = onFullfilledCallback(this.value);
promise.resolve(result);
+ if(result instanceof myPromise) {
+ return result;
+ }
});
this.onRejectedCallbacks.push(() => {
const result = onRejectedCallback(this.value);
promise.reject(result);
+ if(result instanceof myPromise) {
+ return result;
+ }
});
}
})
return promise;
}
}
到这里就结束了
絮叨两句
闲聊两句自己的小感受,以前的时候写代码有种习惯是一开始就思考“最佳实践”,想着这个点怎么照顾、那个点怎么照顾,多个点怎么兼顾...以至于很难开始动手。
现在来看,其实写代码都是走一步看一步,不曾动手何谈优化捏。先把乞丐版的写出来,慢慢去完善就好了。
转载自:https://juejin.cn/post/7258173427340296229