手撕Promise
到了找实习的时候,看面经感觉不是很好,还是写写代码最能记住东西
手撕目标
- 包含then、catch方法
- 支持链式调用
- 异步解决方案
手撕开始
那么首先,如果你不知道Promise,你可能需要绕道先去学习一下,因为这里不想啰嗦要直接动手写滴哦
我这里应该是和网上的很多教程不太一样哈,因为一时没有想到大家的思路......
基本框架
const PENDING = "pending";
const FINISHED = "fulfilled";
const FAILED = "rejected";
class XPromise {
constructor(handleFinish) {
let __PromiseState__ = PENDING;
this.__PromiseState__=__PromiseState__
this.onResolvedCallbacks = [];
this.onRejectCallback = undefined;
this.__PromiseResult__ = undefined;
const resolve = (val) => {
...
};
const reject = (val) => {
...
};
try {
handleFinish?.(resolve, reject);
} catch (error) {
reject(error);
}
}
then(fun,handleError) {
...
}
catch(fun) {
...
}
}
解释一下:
__PromiseState__
我们用这个变量存储Promise对象的状态,上边则预先声明了"pending","fulfilled","rejected"四种状态onResolvedCallBacks
这是一个函数数组,里边存放着尚未执行的来自then()
的函数,之所以用数组是为了保证链式调用onRejectCallback
存放着一个handleError函数,当Promise抛出异常时,执行该函数__PromiseResult__
存放着resolve或者reject的结果
完善resolve、reject、then和catch
- resolve和reject
const resolve = (val) => {
this.__PromiseResult__ = val;
this.__PromiseState__ = FINISHED;
};
this.reject = (val) => {
this.__PromiseResult__ = val;
this.__PromiseState__ = FAILED;
delete this.reject
};
这里在跑一次reject之后就把reject从对象中删除了,是为了遵守Promise规范中reject后不可以修改拒绝的原因
- then和catch
then(fun,handleError) {
try {
if (this.__PromiseState__ === PENDING){
this.onResolvedCallbacks.push(fun);
this.onRejectCallback = handleError||this.onRejectCallback;
}
else if (this.__PromiseState__ === FINISHED) {
let res = fun?.(this.__PromiseResult__);
if (res) this.__PromiseResult__ = res;
}else if (this.__PromiseState__ === FAILED) handleError?.(this.__PromiseResult__)
} catch (error) {
this.reject?.(error);
}
return this;
}
catch(fun) {
return this.then(undefined,fun)
}
then的第一个参数为状态
fulfilled
应该执行的函数fun
,后者则是Promise then第二个参数
在运行时,如果当前state为pending
则将fun
塞入onResolvedCallbacks
,当前state为fulfilled
则直接执行fun
,并且如果fun
有返回值,则将返回值作为新的__PromiseResult__
用setter实现我们的异步
Object.defineProperties(this, {
__PromiseState__: {
set(e) {
__PromiseState__ = e;
if (e === FINISHED) {
try {
this.onResolvedCallbacks.forEach(item =>item?.(this.__PromiseResult__))
} catch (error) {
this.reject(error);
}
} else if (e === FAILED) {
if(this.onRejectCallback instanceof Function) this.onRejectCallback?.(this.__PromiseResult__)
else throw new Error(this.__PromiseResult__)
}
},
get:()=>__PromiseState__
},
});
这里解释下,前文之所以额外定义一个
__PromiseState__
而不是直接使用this.__PromiseState__
是因为getter内部调用该属性同样触发getter
当我们resolve或者reject,会触发setter,也就是这里的set
而我们此时就可以根据状态执行我们的函数了
65行完整代码
const PENDING = "pending";
const FINISHED = "fulfilled";
const FAILED = "rejected";
class XPromise {
constructor(handleFinish) {
let __PromiseState__ = PENDING;
this.__PromiseState__ = __PromiseState__;
this.onResolvedCallbacks = [];
this.__PromiseResult__ = undefined;
const resolve = (val) => {
this.__PromiseResult__ = val;
this.__PromiseState__ = FINISHED;
};
this.reject = (val) => {
this.__PromiseResult__ = val;
this.__PromiseState__ = FAILED;
delete this.reject;
};
Object.defineProperties(this, {
__PromiseState__: {
set(e) {
__PromiseState__ = e;
if (e === FINISHED) {
try {
this.onResolvedCallbacks.forEach((item) => {
let res = item?.(this.__PromiseResult__);
if (res) this.__PromiseResult__ = res;
});
} catch (error) {
this.reject(error);
}
} else if (e === FAILED) {
if (this.onRejectCallback instanceof Function)
this.onRejectCallback?.(this.__PromiseResult__);
else throw new Error(this.__PromiseResult__);
}
},
get: () => __PromiseState__,
},
});
try {
handleFinish?.(resolve, this.reject);
} catch (error) {
this.reject?.(error);
}
}
then(fun, handleError) {
try {
if (this.__PromiseState__ === PENDING) {
this.onResolvedCallbacks.push(fun);
this.onRejectCallback = handleError || this.onRejectCallback;
} else if (this.__PromiseState__ === FINISHED) {
let res = fun?.(this.__PromiseResult__);
if (res) this.__PromiseResult__ = res;
} else if (this.__PromiseState__ === FAILED)
handleError?.(this.__PromiseResult__);
} catch (error) {
this.reject?.(error);
}
return this;
}
catch(fun) {
return this.then(undefined, fun);
}
}
测试
那么我们跑一下,看看有没有效果
测试1:异步与链式调用
function test() {
let p = new XPromise((rs, rj) => {
setTimeout(() => {
rs(2021);
}, 1000);
})
.then((res) => {
console.log(res);
return 2022;
})
.then((res) => {
console.log(res);
});
}
test();
预估输出2021、2022
测试2:异常抛出
function test() {
let p = new XPromise((rs, rj) => {
setTimeout(() => {
rs(2021);
}, 1000);
}).then(res=>{
console.log(res)
const i=1
i=2
})
}
test();
如预料一般,会在1s后输出2021;然后由于我们在then中修改const值,会抛出异常
测试3:catch捕获异常
function test() {
let p = new XPromise((rs, rj) => {
setTimeout(() => {
rs(2021);
}, 1000);
})
.then((res) => {
const i=1
i=0
return 2022;
})
.catch(e=>{
console.log(e)
})
}
test();
既然捕获了,那就不会红色报错啦
那么至此,手撕Promise完成
更多
这里只实现了Promise最基本的功能,至于all、race类似的方法,就不多写啦
- then里的回调无法放置异步函数所以我没有真的实现Promise...还是懂得不够,不行不行等我更新