简介学习之前 需要先对Promise有个基本了解哦,这里都默认大家都是比较熟悉Promise的
本次将带小伙伴们实现Promise的基本功能
Promise的基本骨架Promise的thenPromise.then的多次调用then链式调用catch的实现finally的实现
const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise {constructor(executor) {this.status = PROMISE_STATUS_PENDING;const resolve = (value) => {if (this.status === PROMISE_STATUS_PENDING) {this.status = PROMISE_STATUS_FULFILLED;console.log(value);}}const rejected = (reason) => {if (this.status === PROMISE_STATUS_PENDING) {this.status = PROMISE_STATUS_REJECTED;console.log(reason);}}executor(resolve, rejected)}}// 初步搭建好Promise的construtor结构const promise = new ZXPromise((resolve, rejected) => {resolve("123");rejected("wushichu")})- 因为
Promise有三种状态pending,fulfilled,rejected,我们这里就声明三个常量来代表这三种状态 Promise中需要传递一个回调函数,他的参数中包含了resolve和rejected,调用resolve之后,状态会变为fulfilled,调用rejected,状态会变成rejected- 我定义了一个类,我们在
constructor中定义所需要的resolve和rejected函数,然后将这两个函数传入那个executor中去,这样Promise的基本骨架就已经搭建完成了,非常简单.
const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise { constructor(executor) {this.status = PROMISE_STATUS_PENDING;const resolve = (value) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {//因为只有pending状态才能进行变化if(this.status!==PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_FULFILLED;if (this.onfufilled)this.onfufilled(value);})}}const rejected = (reason) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if(this.status!==PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_REJECTED;if (this.onrejected)this.onrejected(reason);})}}executor(resolve, rejected) } then(onfufilled, onrejected) {this.onfufilled = onfufilled;this.onrejected = onrejected; }}// 接下来开始写then方法const promise = new ZXPromise((resolve, rejected) => { resolve("123"); rejected("wushichu");})promise.then((res) => { console.log("res", res);}, (err) => { console.log("err", err);})then方法中接受两个参数,分别是onfulfilled和onrejected两个函数,分别对应着状态fulfilled和rejected- 这里要注意一个点我在
resolve和rejected中都使用了queueMicrotask,这里使用的目的是为了保证顺序执行的一致性,确保在then方法执行过后,再去执行相关代码,这里需要大家熟悉微任务队列和宏任务队列,推荐大家看下这篇文章
在JS中使用queueMicroTask
undefined【[手写系列] 带你实现一个简单的Promise】
p1.then((res) => { console.log("res1", res);});p1.then((res) => { console.log('res2: ', res);});setTimeout(() => { p1.then((res) => {console.log("res4", res); })}, 1000);现在我们来解决下上述问题,看代码const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise { constructor(executor) {this.status = PROMISE_STATUS_PENDING;this.value = https://tazarkount.com/read/undefined;this.reason = undefined;this.onfufilled = [];this.onrejected = [];const resolve = (value) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_FULFILLED;this.value = value;this.onfufilled.forEach(fn => {fn(value);});})}}const rejected = (reason) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_REJECTED;this.reason = reason;this.onrejected.forEach(fn => {fn(reason);})})}}executor(resolve, rejected) } // 接下来为了Promise能够多次调用 进行优化 then(onfufilled, onrejected) {if (this.status === PROMISE_STATUS_FULFILLED) {onfufilled(this.value);}if (this.status === PROMISE_STATUS_REJECTED) {onrejected(this.value);}if (this.status === PROMISE_STATUS_PENDING) {this.onfufilled.push(onfufilled);this.onrejected.push(onrejected);} }}- 因为改进之后,需要存储
resolve和rejected的value和reason值,所以我们定义了这两个值 - 为了满足多次调用,我们需要将
promise中的onfulfilled和onrejected改为数组存储以用来满足我们的多次调用 - 定时器的问题我这边说下,因为
setTimeout属于宏任务,在同步代码执行完毕之后,会接着执行微任务,所以宏任务是最后来执行的,所以也就造成了promise中的代码执行完了,但是包裹在定时器中的then方法没有获取到结果 - 所以呢,在这里我决定让处于定时器中的代码直接执行而不压入数组中去,因为定时器之前的代码已经执行完毕了,
promise的状态也已经发生了改变,所以我就在then方法中判断promise的状态,如果是fulfilled和rejected状态的话,传过来的函数就直接执行
const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise { constructor(executor) {this.status = PROMISE_STATUS_PENDING;this.value = https://tazarkount.com/read/undefined;this.reason = undefined;this.onfufilled = [];this.onrejected = [];const resolve = (value) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_FULFILLED;this.value = value;this.onfufilled.forEach(fn => {fn(value);});})}}const rejected = (reason) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_REJECTED;this.reason = reason;this.onrejected.forEach(fn => {fn(reason);})})}}try{executor(resolve, rejected)}catch(err){console.log(err);}}then(onfufilled, onrejected) {return new ZXPromise((resolve, rejected) => {if (this.status === PROMISE_STATUS_FULFILLED) {try {//如果then中有返回值,就会作为下一个then所接收的值const value = onfufilled(this.value);resolve(value);} catch (err) {rejected(err);}}if (this.status === PROMISE_STATUS_REJECTED) {try {const value = onrejected(this.value);resolve(value);} catch (err) {rejected(err);}}if (this.status === PROMISE_STATUS_PENDING) {try {this.onfufilled.push(() => {const value = onfufilled(this.value);resolve(value);});} catch (err) {rejected(err);}try {this.onrejected.push(() => {const value = onrejected(this.value);resolve(value);});} catch (err) {rejected(err);}}}) }}const promise = new ZXPromise((resolve, rejected) => { resolve("123"); rejected("wushichu");})promise.then((res) => { console.log("res1:", res); return "abc";}, (err) => { console.log("err1", err);}).then((res) => { console.log("res2", res);}, (err) => { console.log("err2", err);})- 变化最大的就是then方法了,大家可以看到我又把
ZXPromise返回出去了,代码中我写的很清楚了
const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";const execFnWithCatchError = (execFn, value, resolve, reject) => {try {const result = execFn(value);resolve(result);} catch (err) {reject(err);}}class ZXPromise {constructor(executor) {this.status = PROMISE_STATUS_PENDING;this.value = https://tazarkount.com/read/undefined;this.reason = undefined;this.onfufilled = [];this.onrejected = [];const resolve = (value) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_FULFILLED;this.value = value;this.onfufilled.forEach(fn => {fn(value);});})}}const rejected = (reason) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_REJECTED;this.reason = reason;this.onrejected.forEach(fn => {fn(reason);})return this.reason;})}}executor(resolve, rejected)}then(onfufilled, onrejected) {//这一段是为了将错误代码传递下去的const defaultOnRejected = err => { throw err }onrejected = onrejected || defaultOnRejectedreturn new ZXPromise((resolve, rejected) => {if (this.status === PROMISE_STATUS_FULFILLED && onfufilled) {execFnWithCatchError(onfufilled, this.value, resolve, rejected);}if (this.status === PROMISE_STATUS_REJECTED && onrejected) {execFnWithCatchError(onrejected, this.reason, resolve, rejected);}if (this.status === PROMISE_STATUS_PENDING) {if (onfufilled)this.onfufilled.push(() => {execFnWithCatchError(onfufilled, this.value, resolve, rejected);});if (onrejected) {this.onrejected.push(() => {execFnWithCatchError(onrejected, this.reason, resolve, rejected);});}}})}catch(onrejected) {return this.then(undefined, onrejected);}}- 大家可以看到
catch代码实际上就只有一行,就是将then方法进行了调用,是不是相当简单呢 - 然后我觉得那个
try catch代码重复性比较高,所以我将它提取了出来复用 - 然后大家看下那个
then里面的开头,onrejected函数被给予了一个默认值,如果then没有传递第二个参数,那么会被赋予一个错误处理函数的默认值,抛出错误后,会自动被try catch捕获进行reject,这样子错误会被层层传递,一直到最后被catch函数所执行.
finally(fn) {return this.then(() => { fn() }, () => { fn() });}- 在类中加上这一段代码就好了,因为finally是无法接收任何resolve和rejected的值的,所以我们在传递的函数中执行
fn,就是避免resolve的值和rejected的值被传递到finally上去
const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";const execFnWithCatchError = (execFn, value, resolve, reject) => {try {const result = execFn(value);resolve(result);} catch (err) {reject(err);}}class ZXPromise {constructor(executor) {this.status = PROMISE_STATUS_PENDING;this.value = https://tazarkount.com/read/undefined;this.reason = undefined;this.onfufilled = [];this.onrejected = [];const resolve = (value) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_FULFILLED;this.value = value;this.onfufilled.forEach(fn => {fn(value);});})}}const rejected = (reason) => {if (this.status === PROMISE_STATUS_PENDING) {queueMicrotask(() => {if (this.status !== PROMISE_STATUS_PENDING) returnthis.status = PROMISE_STATUS_REJECTED;this.reason = reason;this.onrejected.forEach(fn => {fn(reason);})return this.reason;})}}executor(resolve, rejected)}then(onfufilled, onrejected) {//这一段是为了将错误代码传递下去的const defaultOnRejected = err => { throw err }onrejected = onrejected || defaultOnRejectedreturn new ZXPromise((resolve, rejected) => {if (this.status === PROMISE_STATUS_FULFILLED && onfufilled) {execFnWithCatchError(onfufilled, this.value, resolve, rejected);}if (this.status === PROMISE_STATUS_REJECTED && onrejected) {execFnWithCatchError(onrejected, this.reason, resolve, rejected);}if (this.status === PROMISE_STATUS_PENDING) {if (onfufilled)this.onfufilled.push(() => {execFnWithCatchError(onfufilled, this.value, resolve, rejected);});if (onrejected) {this.onrejected.push(() => {execFnWithCatchError(onrejected, this.reason, resolve, rejected);});}}})}catch(onrejected) {return this.then(undefined, onrejected);}finally(fn) {return this.then(() => { fn() }, () => { fn() });}}- 大家可以自行进行测试
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
