初步实现Promise雏形
- 实现Promise之前,先看一下promise是如何使用的,再根据它的使用方法,来一步步的实现。首先,Promise就是一个用来存储数据的容器,它拥有着一套特殊的存取数据的方式,这个方式使得它里面可以存储异步调用的结果。创建Promise时,构造函数中需要一个函数作为参数, Promise构造函数的回调函数,他会在创建Promise时调用,调用时会有两个参数传递进去(resolve、reject),resolve 和reject是两个函数,通过这两个函数可以向Promise中存储数据,resolve在执行正常时存储数据,reject在执行错误时存储数据。可以通过Promise的实例方法then来读取Promise中存储的数据,then需要两个回调函数作为参数,回调函数用来获取Promise中的数据,通过resolve存储的数据,会调用第一个函数返回,可以在第一个函数中编写处理数据的代码,通过reject存储的数据或者出现异常时,会调用第二个函数返回可以在第二个函数中编写处理异常的代码。
- Promise中维护了两个隐藏属性,PromiseResult用来存储数据,PromiseState记录Promise的状态(三种状态)。pending(进行中)fulfilled(完成) 通过resolve存储数据时,rejected(拒绝,出错了) 出错了或者通过reject存储数据时。
- 当Promise创建时,PromiseState初始值为pending,当通过resolve存储数据时 PromiseState 变为fulfilled(完成),PromiseResult变为存储的数据。 当通过reject存储数据或出错时 PromiseState 变为rejected(拒绝,出错了), PromiseResult变为存储的数据 或 异常对象。
- 当我们通过then读取数据时,相当于为Promise设置了回调函数,如果PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据,如果PromiseState变为rejected,则调用then的第二个回调函数来返回数据
来看一下我们写的Promise要达到的效果(我们写的Promise叫MyPromise)
const mp = new MyPromise((resolve, reject) => {
resolve("孙悟空")
})
mp.then((result) => {
console.log('读取数据', result)
})
初步实现Promise代码
const PROMISE_STATE = {
PENDING: 0,
FULFILLED: 1,
REJECTED: 2
}
class MyPromise {
// 创建一个变量用来存储Promise的结果
#result
// 创建一个变量来记录Promise的状态
#state = PROMISE_STATE.PENDING // pending 0 fulfilled 1 rejected 2
constructor(executor) {
// 接受一个 执行器 作为参数
executor(this.#resolve.bind(this), this.#reject.bind(this)) //调用回调函数
}
// 私有的resolve()用来存储成功的数据
#resolve (value) {
// 禁止值被重复修改
// 如果state不等于0,说明值已经被修改 函数直接返回
if (this.#state !== PROMISE_STATE.PENDING) return
this.#result = value
this.#state = PROMISE_STATE.FULFILLED //数据填充成功
}
/* #resolve = () => {
console.log(this)
} */
// 私有的reject()用来存储拒绝的数据
#reject (reason) {
if (this.#state !== PROMISE_STATE.PENDING) return
this.#result = value
this.#state = PROMISE_STATE.REJECTED
}
// 添加一个用来读取数据的then方法
then (onFulfilled, onRejected) {
if (this.#state === PROMISE_STATE.FULFILLED) {
onFulfilled(this.#result)
}else if(this.#state === PROMISE_STATE.REJECTED){
onRejected(this.#result)
}
}
}
这里resolve和reject前面加的#是为了作为类的私有方法,防止在外面能访问到。另外,#resolve和#reject中的this,如果没有在constructor类的构造函数中给#resolve和#reject用bind绑定this,#resolve和#reject中的this会指向undefined。除了使用bind绑定this以外,还可以用箭头函数来实现#resolve和#reject,效果是一样的。
思考:当前的Promise实现还有什么问题
const mp = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("孙悟空")
}, 1000)
})
mp.then((result) => {
console.log('读取数据', result)
})
执行new MyPromise的时候,如果用一个定时器setTimeout过1秒才给resolve赋值,当mp.then的时候,这个时候this.#state还是PROMISE_STATE.PENDING的状态,这就导致我们会读取不到数据。让我们来思考一下,如何解决这个问题。
解决第一个问题
我们可以创建一个变量来接收一下then的回调函数,当resolve的时候再执行它。
const PROMISE_STATE = {
PENDING: 0,
FULFILLED: 1,
REJECTED: 2
}
class MyPromise {
// 创建一个变量用来存储Promise的结果
#result
// 创建一个变量来记录Promise的状态
#state = PROMISE_STATE.PENDING // pending 0 fulfilled 1 rejected 2
// 创建一个变量来存储回调函数
#callback
constructor(executor) {
// 接受一个 执行器 作为参数
executor(this.#resolve.bind(this), this.#reject.bind(this)) //调用回调函数
}
// 私有的resolve()用来存储成功的数据
#resolve (value) {
// 禁止值被重复修改
// 如果state不等于0,说明值已经被修改 函数直接返回
if (this.#state !== PROMISE_STATE.PENDING) return
this.#result = value
this.#state = PROMISE_STATE.FULFILLED //数据填充成功
// 当resolve执行时,说明数据已经进来了,需要调用then的回调函数
this.#callback && this.#callback(this.#result)
}
/* #resolve = () => {
console.log(this)
} */
// 私有的reject()用来存储拒绝的数据
#reject (reason) {
if (this.#state !== PROMISE_STATE.PENDING) return
this.#result = value
this.#state = PROMISE_STATE.REJECTED
}
// 添加一个用来读取数据的then方法
then (onFulfilled, onRejected) {
if (this.#state === PROMISE_STATE.PENDING) {
// 进入判断说明数据还没有进入Promise,将回调函数设置为callback的值
this.#callback = onFulfilled
} else if (this.#state === PROMISE_STATE.FULFILLED) {
/**
* 目前来讲,then只能读取已经存储进Promise的数据
* 而不能读取一部存储的数据
*/
onFulfilled(this.#result)
} else if (this.#state === PROMISE_STATE.REJECTED) {
onRejected(this.#result)
}
}
}
思考:当前的Promise实现还有什么问题
const mp = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("孙悟空")
}, 1000)
})
mp.then((result) => {
console.log('读取数据1', result)
})
mp.then((result) => {
console.log('读取数据2', result)
})
mp.then((result) => {
console.log('读取数据3', result)
})
想一下,上面的代码运行结果会是什么,为什么? 运行结果:读取数据3,孙悟空 因为.then()的时候第三个回调函数覆盖了第一、第二个。
解决第二个问题
既然有可能会有多个.then的回调函数的话,那么接收then的回调函数也得是一个数组callbacks了。另外,之前 都是直接调用的,then的回调函数,应该放入到微任务队列中执行,而不是直接调用。
const PROMISE_STATE = {
PENDING: 0,
FULFILLED: 1,
REJECTED: 2
}
class MyPromise {
// 创建一个变量用来存储Promise的结果
#result
// 创建一个变量来记录Promise的状态
#state = PROMISE_STATE.PENDING // pending 0 fulfilled 1 rejected 2
// 创建一个变量来存储回调函数
// 由于回调函数可能有多个,所以我们使用数组来存储回调函数
#callbacks = []
constructor(executor) {
// 接受一个 执行器 作为参数
executor(this.#resolve.bind(this), this.#reject.bind(this)) //调用回调函数
}
// 私有的resolve()用来存储成功的数据
#resolve (value) {
// 禁止值被重复修改
// 如果state不等于0,说明值已经被修改 函数直接返回
if (this.#state !== PROMISE_STATE.PENDING) return
this.#result = value
this.#state = PROMISE_STATE.FULFILLED //数据填充成功
// 当resolve执行时,说明数据已经进来了,需要调用then的回调函数
queueMicrotask(() => {
// 调用callbacks中的所有函数
this.#callbacks.forEach(cb => {
cb()
})
})
}
/* #resolve = () => {
console.log(this)
} */
// 私有的reject()用来存储拒绝的数据
#reject (reason) {
}
// 添加一个用来读取数据的then方法
then (onFulfilled, onRejected) {
if (this.#state === PROMISE_STATE.PENDING) {
// 进入判断说明数据还没有进入Promise,将回调函数设置为callback的值
// this.#callback = onFulfilled
this.#callbacks.push(() => {
onFulfilled(this.#result)
})
} else if (this.#state === PROMISE_STATE.FULFILLED) {
/**
* 目前来讲,then只能读取已经存储进Promise的数据
* 而不能读取一部存储的数据
*/
// onFulfilled(this.#result)
/**
* then的回调函数,应该放入到微任务队列中执行,而不是直接调用
*/
queueMicrotask(() => {
onFulfilled(this.#result)
})
}
}
}
思考:看下面的代码,当前的MyPromise可以链式调用吗?
const mp = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("孙悟空")
}, 1000)
})
let result = mp.then((result) => {
console.log('读取数据1', result)
return '猪八戒'
}).then(r => {
console.log("读取数据2", r)
return "沙和尚"
}).then(r => {
console.log("读取数据3", r)
})
解决第三个问题
如何让MyPromise可以链式调用,需要我们使得MyPromise中的then方法的返回值返回一个新的MyPromise,并且在里面用resolve接收一下onFulFilled(this.#result)的返回值。最后代码如下:
const PROMISE_STATE = {
PENDING: 0,
FULFILLED: 1,
REJECTED: 2
}
class MyPromise {
// 创建一个变量用来存储Promise的结果
#result
// 创建一个变量来记录Promise的状态
#state = PROMISE_STATE.PENDING // pending 0 fulfilled 1 rejected 2
// 创建一个变量来存储回调函数
// 由于回调函数可能有多个,所以我们使用数组来存储回调函数
#callbacks = []
constructor(executor) {
// 接受一个 执行器 作为参数
executor(this.#resolve.bind(this), this.#reject.bind(this)) //调用回调函数
}
// 私有的resolve()用来存储成功的数据
#resolve (value) {
// 禁止值被重复修改
// 如果state不等于0,说明值已经被修改 函数直接返回
if (this.#state !== PROMISE_STATE.PENDING) return
this.#result = value
this.#state = PROMISE_STATE.FULFILLED //数据填充成功
// 当resolve执行时,说明数据已经进来了,需要调用then的回调函数
queueMicrotask(() => {
// 调用callbacks中的所有函数
this.#callbacks.forEach(cb => {
cb()
})
})
}
/* #resolve = () => {
console.log(this)
} */
// 私有的reject()用来存储拒绝的数据
#reject (reason) {
}
// 添加一个用来读取数据的then方法
then (onFulfilled, onRejected) {
/**
* 谁将成为then返回的新Promise中的数据???
*/
return new MyPromise((resolve, reject) => {
if (this.#state === PROMISE_STATE.PENDING) {
// 进入判断说明数据还没有进入Promise,将回调函数设置为callback的值
// this.#callback = onFulfilled
this.#callbacks.push(() => {
resolve(onFulfilled(this.#result))
})
} else if (this.#state === PROMISE_STATE.FULFILLED) {
/**
* 目前来讲,then只能读取已经存储进Promise的数据
* 而不能读取一部存储的数据
*/
// onFulfilled(this.#result)
/**
* then的回调函数,应该放入到微任务队列中执行,而不是直接调用
* then中回调函数的返回值,会成为新的Promise中的数据
*/
queueMicrotask(() => {
resolve(onFulfilled(this.#result))
})
}
})
}
}
以上就是手写Promise的全部内容了。