前言
今天我们要来实现一个手写的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
}
}
const reject = (reason) => {
if (this.state === MyPromise.PENDING) { // 同上
this.state = MyPromise.REJECTED
this.reason = reason
}
}
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) {
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) }
)
可以看到原生的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);
}catch (error) {
reject(error);
}
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) {
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’)–输出1
- console.log(‘2’)–输出2
- setTimeout()放入宏任务队列
- 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
this.onFulfilledCallbacks.forEach(callback => callback(value))
}
}
const reject = (reason) => {
if (this.state === MyPromise.PENDING) { // 同上
this.state = MyPromise.REJECTED
this.reason = reason
this.onRejectedCallbacks.forEach(callback => callback(reason))
}
}
try {
executor(resolve, reject)
}catch (error) {
reject(error)
}
}
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
,为什么是数组呢,因为可能.then
后面又接.then
,状态都为pending
,然后我们把它们放入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) {
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)
resolve(result)
})
}
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
this.onFulfilledCallbacks.forEach(callback => callback(value))
}
}
const reject = (reason) => {
if (this.state === MyPromise.PENDING) { // 同上
this.state = MyPromise.REJECTED
this.reason = reason
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)
resolve(result)
}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)
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有帮助,如有错误,疏漏的地方恳请指出,感谢你的阅读!
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=20187,转载请注明出处。
评论0