全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2025-01-19_带你一步一步手写Promise(保姆级讲解,你看你也会写)

您的位置:首页 >> 新闻 >> 行业资讯

带你一步一步手写Promise(保姆级讲解,你看你也会写) 点击关注公众号,“技术干货”及时达! 前言今天我们要来实现一个手写的Promise。如果你对JavaScript的Promise还不是很熟悉,或者想深入了解它的内部机制,那么这篇文章非常适合你,跟着我从零开始,一步步构建Promise吧! 正文1.初始结构创建类 let promise=new Promise((resolve,reject)={})我们通常用Promise都是这样new一个实例对吧,那我们就用class创建一个Promise类,如下: class MyPromise { constructor(executor) { const resolve = (value) = const reject = (reason) = executor(resolve, reject); } 我们可以看到上面的例子,我们在new一个promise实例的时候,肯定是需要传入参数的,这个参数是一个函数,而且当我们传入这个函数参数的时候,这个函数参数会被自动执行,所以我们在类的construct里面添加一个参数exector,并且在里面执行一下这个参数,因为原生Promsie里面可以传入传入resolve和reject两个参数,所有我们创建两个函数resolve和reject,并把它传入exector。 创建所需属性和方法 let promise=new Promise((resolve,reject)={ resolve('成功') })例子中我们知道resolve()可以改变promsie状态,promsie有三个状态,分别是pending、fulfilled、rejected,并且呢只能是pending=fulfilled,pending=rejected,其它都不可以,所有我们提前把这些状态定义好,我们就用static来创建静态属性,并且在constructor里面添加一个state(类里面的this是指向new出来的实例对象的)状态为MyPromise.PENDING(静态属性可以通过类名.属性来访问到)也就是pending状态,这样每个实例创建后就会有自生的属性来判断及变动了,并且我们就可以在自己写的resolve和reject函数里面来改变状态了 class MyPromise { static PENDING = 'pending'; static FULFILLED = 'fulfilled'; static REJECTED = 'rejected'; constructor(executor) { this.state = MyPromise.PENDING; this.value = undefined; this.reason = undefined; const resolve = (value) = { if (this.state === MyPromise.PENDING) { // fulfilled状态的上一种状态只能是 pending,状态一经变更就不在逆转 this.state = MyPromise.FULFILLED; this.value = value; // 保存resolve的参数,留作then中使用 } }; const reject = (reason) = { if (this.state === MyPromise.PENDING) { // 同上 this.state = MyPromise.REJECTED; this.reason = reason; // 保存reject的参数,留作then中使用 } }; executor(resolve, reject); } }看上面原生的promise例子可以知道,resolve和reject是可以传入参数的,所有分别创建两个value和reason作为它俩的参数,并且我们把参数赋值给实例的valu和reason属性。 2.then的实现我们接着来实现then方法 let promise=new Promise((resolve,reject)={ resolve('成功') reject('失败') }) promise.then( value=console.log(value), // 输出: 成功 reason=console.log(reason) ) let promise1=new Promise((resolve,reject)={ resolve('成功') }) promise1.then( value={console.log(value)}, // 输出: 成功 reason={console.log(reason)} ) let promise2=new Promise((resolve,reject)={ reject('失败') }) promise2.then( value={console.log(value)}, reason={console.log(reason)} // 输出: 失败 ) !---- class MyPromise { ...前面代码一样 then(onFulfilled, onRejected) { if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled onFulfilled(this.value); } if (this.state === MyPromise.REJECTED) { onRejected(this.reason) } } }因为then是在创建实例后再进行调用的,因此我们在constructor外面创建一个then方法,看到上面例子中可以发现原生Promise的then方法是有两个参数的,且都是回调函数的,then中的第二个回调充当了catch一样的效果,在Promise状态变成更为rejected时触发的,只不过后来加了一个catch,因此我们给手写的then里面添加两个参数onFulfilled, onRejected,分别为状态为成功时和拒绝时,并且看到上面例子中只会执行成功状态或失败状态的其中一个,因此我们手写时就要判断状态是什么,再执行相应状态的函数,并且分别为它们传入之前在resolve或rejrct中保留的值value或reason。 3.解决执行异常情况一因为原生的Promise考虑到了很多情况,因此我们要改进我们的Promise // 原生 let promise = new Promise((resolve, reject) = { throw new Error('失败test'); }) promise.then( value = { console.log(value) }, reason = { console.log(reason.message) } // 输出; 失败test )可以看到原生的promsie里面调用then方法时可以把错误的信息输出出来,再来看看我们写的 // 手写 let promise = new MyPromise((resolve, reject) = { throw new Error('失败test'); }) promise.then( value = { console.log(value) }, reason = { console.log(reason.message) } ) 可以看到报错了 class MyPromise { //...其它代码不变 try { executor(resolve, reject); // 执行executor函数,传入resolve和reject两个参数 }catch (error) { reject(error); // 捕获executor中抛出的异常,并执行reject } then(){}} 所以呢我们在执行resolve和reject时,进行判断,如果没有报错就正常执行,如果报错就把错误信息传给reject方法,并且执行reject方法,这样就不会出现上面的问题. 情况二 // 原生 let promise = new Promise((resolve, reject) = { resolve('成功') }) promise.then( undefined, // 执行是没有问题的 reason = { console.log(reason.message) } )可以看到原生的promise的then里面的两个参数如果不是函数的话,是被忽略的,执行没有问题,再来看看我们的 // 手写 let promise = new MyPromise((resolve, reject) = { resolve('成功') }) promise.then( undefined, reason = { console.log(reason.message) } ) 报错了 class MyPromise { //...前面代码一样 then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value = value; onRejected = typeof onRejected === 'function' ? onRejected : reason = { throw reason }; if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled onFulfilled(this.value); } if (this.state === MyPromise.REJECTED) { onRejected(this.reason) } } }所以我们就用三元运算符来判断,如果是函数就把原来的函数赋给它,如果不是函数就把它用函数包着,返回它或把它抛出就可以了。 4.实现异步功能在对代码进行了一个基本修补后,我们就可以来实现promise的异步功能了,我们来一个看原生的promise代码: // 原生 console.log('1') let promise = new Promise((resolve, reject) = { console.log('2') resolve('成功') }) promise.then( value = { console.log(value)}, reason = { console.log(reason)} ) console.log('3') // 手写 console.log('1') let promise = new MyPromise((resolve, reject) = { console.log('2') resolve('成功') }) promise.then( value = { console.log(value)}, reason = { console.log(reason)} ) console.log('3') class MyPromise { //...前面代码一样 then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value = value; onRejected = typeof onRejected === 'function' ? onRejected : reason = { throw reason }; if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled setTimeout(() = { onFulfilled(this.value); }) } if (this.state === MyPromise.REJECTED) { setTimeout(() = { onRejected(this.reason); }) } } }原生promise是异步的并且是微任务,而我们手写的promise全是同步代码,为了达到这个效果,我们这里方便一点就用setTimeout来模拟这个异步效果,我们在进行if状态判别后给代码添加setTimeout,要不然状态不符合添加异步也是没有意义的。 改完代码的执行结果: 但是异步的问题我们真的解决了吗?我们接着往下看。 5.实现回调保存这里将要进入难点部分,我们来一个看原生的promise代码: console.log('1') let promise = new Promise((resolve, reject) = { console.log('2') setTimeout(() = { resolve('成功') console.log('4') }) }) promise.then( value = { console.log(value)}, reason = { console.log(reason)} ) console.log('3') 再来看看我们手写的输出结果 console.log('1') let promise = new MyPromise((resolve, reject) = { console.log('2') setTimeout(() = { resolve('成功') console.log('4') }) }) promise.then( value = { console.log(value)}, reason = { console.log(reason)} ) console.log('3') 诶,没有打印resolve的结果对吧,我们先来捋一捋原生promise的执行过程: console.log('1')--输出1console.log('2')--输出2setTimeout()放入宏任务队列promise.then()放入微任务队列console.log('3')--输出3执行微任务,发现resolve()没执行,promise状态没有改变,还是pending状态,那么就不执行执行宏任务,resolve()把状态变为fulfilled,执行console.log('4')--输出4最后执行.then--输出成功再来捋一捋手写的promise,没有输出成功的原因是当我们执行到then方法时,我们then方法是根据条件来执行代码的,也就是说没有符合的情况也就是没有符合的状态,也就是没有情况对应pending状态对吧,总的来说就是我们的then方法没有能resolve执行完状态改变后再执行自己的能力,那改进吧 class MyPromise { static PENDING = 'pending'; static FULFILLED = 'fulfilled'; static REJECTED = 'rejected'; constructor(executor) { this.state = MyPromise.PENDING; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) = { if (this.state === MyPromise.PENDING) { // fulfilled状态的上一种状态只能是 pending,状态一经变更就不在逆转 this.state = MyPromise.FULFILLED; this.value = value; // 保存resolve的参数,留作then中使用 this.onFulfilledCallbacks.forEach(callback = callback(value)); } }; const reject = (reason) = { if (this.state === MyPromise.PENDING) { // 同上 this.state = MyPromise.REJECTED; this.reason = reason; // 保存reject的参数,留作then中使用 this.onRejectedCallbacks.forEach(callback = callback(reason)); } }; try { executor(resolve, reject); // 执行executor函数,传入resolve和reject两个参数 }catch (error) { reject(error); // 捕获executor中抛出的异常,并执行reject } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value = value; onRejected = typeof onRejected === 'function' ? onRejected : reason = { throw reason }; if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled setTimeout(() = { onFulfilled(this.value); }) } if (this.state === MyPromise.REJECTED) { setTimeout(() = { onRejected(this.reason); }) } if(this.state === MyPromise.PENDING){ this.onFulfilledCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); } } }我们在then里面增加一种情况,当then在resolve或reject执行前被执行时也就是pending状态,我们把then方法中对应的回调函数放入对应数组中,我们再上面再定义两个数组分别为onFulfilledCallbacks和onRejectedCallbacks,为什么是数组呢,因为可能出现多个相同promise.then()的情况,状态都为pending,如下面这个代码: promise.then(value = console.log()); promise.then(value = console.log()); promise.then(value = console.log());然后我们把它们放入resolve和reject中,当resolve和reject执行时,去遍历调掉数组里面的回调函数是不是实现了上面我们想要的效果,只能说秒呀,总结就是pending状态时把回调放到resolve或reject中去执行 我们来看执行结果: 诶,先输出成功,这是因为resolve里面都是同步代码所有先执行了resolve(),所以我们要想办法把resolve里面异步执行数组里面的函数就可以解决这个问题 class MyPromise { //... 省略前面的代码 then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value = value; onRejected = typeof onRejected === 'function' ? onRejected : reason = { throw reason }; if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled setTimeout(() = { onFulfilled(this.value); }) } if (this.state === MyPromise.REJECTED) { setTimeout(() = { onRejected(this.reason); }) } if(this.state === MyPromise.PENDING){ this.onFulfilledCallbacks.push(value = { setTimeout(() = { onFulfilled(value); }) }) this.onRejectedCallbacks.push(reason={ setTimeout(()={ onRejected(reason); }) }) } } }我们通过在把回调函数放入数组时,把它放进一个setTimeout里面,那是不是数组里面的方法都变成异步了,当resolve执行数组里面的函数就是异步了,那么不就实现了吗,真正实现了和原生promise一样的效果,resolve执行完毕后把状态变为fulfilled或rejected才执行.then呀 我们来看效果: 完美 6.实现链式效果来到我们最后一步,完成.then的链式功能,也就.then后面接.then class MyPromise { //... 省略前面的代码 then(onFulfilled, onRejected) { // 判断传入 then 中的参数是否为函数类型,如果是那顺利执行,否则我们人为写入一个函数 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value = value; onRejected = typeof onRejected === 'function' ? onRejected : reason = { throw reason }; // then的执行结果要返回一个新的promise return new MyPromise((resolve, reject) = { if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled setTimeout(() = { const result = onFulfilled(this.value); // then自行调用自己的回调函数,接收这次then回调函数里面return出来的值 resolve(result); // 并把下一个then的状态改为fulfilled,把下次then的回调函数里面的参数保存好 }); } if (this.state === MyPromise.REJECTED) { setTimeout(() = { const result = onRejected(this.reason) reslove(result); }); } if (this.state === MyPromise.PENDING) { // 调用then的Promise对象状态没有变更,则缓存then中的回调 this.onFulfilledCallbacks.push(value = { setTimeout(() = { const result = onFulfilled(value); resolve(result); }); }); this.onRejectedCallbacks.push(reason = { setTimeout(() = { const result = onRejected(reason); reslove(result); }); }); } }); } }要想后面.then能接.then,那then方法里面得返回一个promise实例吧,因为原生的then里面return出来一个值,会当作下一个then里面回调函数里面的参数,所以我们const result = onFulfilled(this.value);把这次的then执行掉,并接收这次then里面回调函数里面return出来的值,再resolve(result);把下一个then的状态改为fulfilled,把下次then的回调函数里面的参数保存好,reject是一样的,因为我们每次都是创建一个新的promise对象,每次数组都是不一样的,所有不用担心。 console.log('1') let promise = new MyPromise((resolve, reject) = { console.log('2') setTimeout(() = { resolve('成功') console.log('4') }) }) promise.then(value = { console.log(value) return 'hello' }).then(value={ console.log(value) }) console.log('3')用我们的promise来执行这段代码 来看最终效果吧: ok,大功告成呀! 这里还有一份更完善的代码,大体思路是不变的,只是加了点try,catch,你就可以.then里面抛出错误了。 class MyPromise { static PENDING = 'pending'; static FULFILLED = 'fulfilled'; static REJECTED = 'rejected'; constructor(executor) { this.state = MyPromise.PENDING; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) = { if (this.state === MyPromise.PENDING) { // fulfilled状态的上一种状态只能是 pending,状态一经变更就不在逆转 this.state = MyPromise.FULFILLED; this.value = value; // 保存resolve的参数,留作then中使用 this.onFulfilledCallbacks.forEach(callback = callback(value)); // then中的回调之在此处已经调用,并接受了参数 } }; const reject = (reason) = { if (this.state === MyPromise.PENDING) { // 同上 this.state = MyPromise.REJECTED; this.reason = reason; // 保存reject的参数,留作then中使用 this.onRejectedCallbacks.forEach(callback = callback(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { // 判断传入 then 中的参数是否为函数类型,如果是那顺利执行,否则我们人为写入一个函数 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value = value; onRejected = typeof onRejected === 'function' ? onRejected : reason = { throw reason }; // then的执行结果要返回一个新的promise return new MyPromise((resolve, reject) = { if (this.state === MyPromise.FULFILLED) { // 调用then的Promise对象状态已经变更为 fulfilled setTimeout(() = { try{ const result = onFulfilled(this.value); // then自行调用自己的回调函数,接收这次then回调函数里面return出来的值 resolve(result); // 并把下一个then的状态改为fulfilled,把下次then的回调函数里面的参数保存好 }catch(error){ reject(error) } }); } if (this.state === MyPromise.REJECTED) { setTimeout(() = { try{ const result = onRejected(this.reason) resolve(result); }catch(error){ reject(error) } }); } if (this.state === MyPromise.PENDING) { // 调用then的Promise对象状态没有变更,则缓存then中的回调 this.onFulfilledCallbacks.push(value = { setTimeout(() = { try{ const result = onFulfilled(value); //if判断是不是promise,这里可能return出一个promise resolve(result); }catch(error){ reject(error) } }); }); this.onRejectedCallbacks.push(reason = { setTimeout(() = { try{ const result = onRejected(reason); reject(result); }catch(error){ reject(error) } }); }); } }); } }例子: console.log('1') let promise = new MyPromise((resolve, reject) = { console.log('2') setTimeout(() = { resolve('成功') console.log('4') }) }) promise.then(value = { console.log(value) return 'hello' }).then(value={ console.log(value) throw new Error('失败') }).then( value=console.log(value), reason=console.log(reason.message) //输出: 失败 ) console.log('3')执行结果: 最后提一嘴,还有一种场景就是then里面返回了一个new Promise,聪明的你肯定能想到如何解决 总结本文到这里就结束了,希望对你手写Promise有帮助,如有错误,疏漏的地方恳请指出,感谢你的阅读! 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2024-01-15_来接单 | 加油及充电产品找设备外观工业设计公司 下一篇:2025-07-06_集成20+先进算法,优于GPT-4o,自主因果分析智能体来了

TAG标签:

18
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价