1、前言
本篇文章,默认你已经知道什么是 Promise ,然后我会带你一步步的实现一个简易的 Promise。将会以循序渐进的方式,分步骤实现。
本文章相关代码地址:https://github.com/layouwen/blog_demo_lpromise
如果本文章对你有所帮助,请不要吝啬你的 Start 哦~
2、三种状态
Promise 它一共会有三种状态:
- pending
- fulfilled
- rejected
下面我们自己实现一个类,默认为 pending 状态,通过调用 resolve 或者 reject 改变其状态
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedcallbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = res}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = err}}console.log(new LPromise((resolve, reject) => console.log('pending'))) // pending 状态const l1 = new LPromise((resolve, reject) => {resolve('我调用了resolve')})console.log(l1) // fulfilled 状态const l2 = new LPromise((resolve, reject) => {reject('我调用了reject')})console.log(l2) // rejected 状态
3、实现 then 参数回调
返回的 Promise ,可以通过使用 then 传递成功和失败的回调。
通过 then 接收了两个回调。实现了分别调用回调的内容。但是发现,他们两个都会执行。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedcallbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = res}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = err}/* new content start */then(onResolve, onReject) {onResolve()onReject()}/* new content end */}const l1 = new LPromise((resolve, reject) => resolve())l1.then(res => console.log('res'),err => console.log('err'))
对执行时机进行调整。使其在调用 resolve 或 reject 才执行相关的回调
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedcallbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = res/* new content start */this.cbResolve() // 报错/* new content end */}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = err/* new content start */this.cbReject() // 报错/* new content end */}then(onResolve, onReject) {/* new content start */this.cbResolve = onResolvethis.cbReject = onReject/* new content end */}}const l1 = new LPromise((resolve, reject) => resolve())l1.then(res => console.log('res'),err => console.log('err'))
改装后,发现 resolve 和 reject 的执行时间比 then 的回调要快。导致无法执行 then 中的回调。我们需要对 resolve 和 reject 中执行回调的部分进行 延迟执行。可以使用 setTimeout 进行延迟
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedcallbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = res/* new content start */setTimeout(() => this.cbResolve())/* new content end */}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = err/* new content start */setTimeout(() => this.cbReject())/* new content end */}then(onResolve, onReject) {this.cbResolve = onResolvethis.cbReject = onReject}}const l1 = new LPromise((resolve, reject) => resolve())l1.then(res => console.log('res'),err => console.log('err'))
考虑到 微任务 和 宏任务。我们可以使用 MutationObserver 替代 setTimeout
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedcallbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = res/* new content start */const run = () => this.cbResolve()const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')/* new content end */}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = err/* new content start */const run = () => this.cbReject()const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')/* new content end */}then(onResolve, onReject) {this.cbResolve = onResolvethis.cbReject = onReject}}const l1 = new LPromise((resolve, reject) => resolve())l1.then(res => console.log('res'),err => console.log('err'))
4、链式调用
在原本的 Promise 中。我们是可以使用 then 链式调用。意味着每个 then 都返回一个新的 Promise。
因为支持链式。所以我们之前的 cbResolve 和 cbReject 就不能单单保存一个回调。要改回一个数组,将每一个 then 中的回调。都保存到回调队列中。等待调用 resolve 或者 reject 后才执行所有回调函数。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefined/* new content start */this.cbResolveQueue = []this.cbRejectQueue = []/* new content end */callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = res/* new content start */const run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {cbFn && cbFn()}}/* new content end */const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = err/* new content start */const run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {cbFn && cbFn()}}/* new content end */const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {/* new content start */return new LPromise((resolve, reject) => {const cbResolve = () => {onResolve && onResolve()resolve()}this.cbResolveQueue.push(cbResolve)const cbReject = () => {onReject && onReject()reject()}this.cbRejectQueue.push(cbReject)})/* new content end */}}const l1 = new LPromise((resolve, reject) => resolve())l1.then(res => console.log('res'),err => console.log('err')).then(res => console.log('res'),err => console.log('err'))
此时我们已经完成了链式调用,但是我们会发现,此时如果返回一个新的 Promise ,却无法获取 Promise 的结果。所以我们得加一些判断条件。我们也会发现,此时此刻我们无法接收到 res 或 err 的参数。所以我们也要完善一下参数传递问题。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedthis.cbResolveQueue = []this.cbRejectQueue = []callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = resconst run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {/* new content start */cbFn && cbFn(res)/* new content end */}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = errconst run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {/* new content start */cbFn && cbFn(err)/* new content end */}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {return new LPromise((resolve, reject) => {/* new content start */const cbResolve = res => {const resolveRes = onResolve && onResolve(res)if (resolveRes instanceof LPromise) {resolveRes.then(resolve)} else {resolve(res)}}/* new content end */this.cbResolveQueue.push(cbResolve)/* new content start */const cbReject = err => {onReject && onReject(err)reject(err)}/* new content end */this.cbRejectQueue.push(cbReject)})}}const l1 = new LPromise((resolve, reject) => resolve('我是传入的 resolve 数据'))l1.then(res => {console.log('第一个then的res', res)return new LPromise((resolve, reject) => resolve('返回的Promise'))},err => console.log('第一个then的err', err)).then(res => console.log('第二个then的res', res),err => console.log('第二个then的err', err)).then(res => console.log('第三个then的res', res),err => console.log('第三个then的err', err))
到现在我们已经实现了 then 的链式调用
5、实现 catch 方法
在调用 catch 的时候自动在回调队列中添加一个错误回调函数。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedthis.cbResolveQueue = []this.cbRejectQueue = []callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = resconst run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {cbFn && cbFn(res)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = errconst run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {cbFn && cbFn(err)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {return new LPromise((resolve, reject) => {const cbResolve = res => {const resolveRes = onResolve && onResolve(res)if (resolveRes instanceof LPromise) {resolveRes.then(resolve)} else {resolve(res)}}this.cbResolveQueue.push(cbResolve)const cbReject = err => {onReject && onReject(err)reject(err)}this.cbRejectQueue.push(cbReject)})}/* new content start */catch(err) {this.then(undefined, err)}/* new content end */}const p1 = new LPromise((resolve, reject) => reject('我是p1的错误信息'))p1.then(res => console.log(res)).catch(err => console.log(err))
6、resolve 和 reject 静态方法
这两个静态方法比较简单。只需要返回一个固定状态的 Promise 即可。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedthis.cbResolveQueue = []this.cbRejectQueue = []callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = resconst run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {cbFn && cbFn(res)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = errconst run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {cbFn && cbFn(err)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {return new LPromise((resolve, reject) => {const cbResolve = res => {const resolveRes = onResolve && onResolve(res)if (resolveRes instanceof LPromise) {resolveRes.then(resolve)} else {resolve(res)}}this.cbResolveQueue.push(cbResolve)const cbReject = err => {onReject && onReject(err)reject(err)}this.cbRejectQueue.push(cbReject)})}/* new content start */static resolve(res) {return new LPromise(resolve => resolve(res))}static reject(err) {return new LPromise((undefined, reject) => reject(err))}/* new content end */}const p1 = LPromise.resolve('成功')console.log(p1)const p2 = LPromise.reject('失败')console.log(p2)
7、实现 finally 方法
这个与 catch 类似的实现,只需要保证不管成功还是失败都执行里面的回调。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedthis.cbResolveQueue = []this.cbRejectQueue = []callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = resconst run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {cbFn && cbFn(res)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = errconst run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {cbFn && cbFn(err)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {return new LPromise((resolve, reject) => {const cbResolve = res => {const resolveRes = onResolve && onResolve(res)if (resolveRes instanceof LPromise) {resolveRes.then(resolve)} else {resolve(res)}}this.cbResolveQueue.push(cbResolve)const cbReject = err => {onReject && onReject(err)reject(err)}this.cbRejectQueue.push(cbReject)})}catch(err) {this.then(undefined, err)}/* new content start */finally(callback) {this.then(callback, callback)}/* new content end */static resolve(res) {return new LPromise(resolve => resolve(res))}static reject(err) {return new LPromise((undefined, reject) => reject(err))}}const p1 = new LPromise((resolve, reject) => reject('我是p1的错误信息'))p1.then(res => console.log(res),err => console.log(err)).finally(() => console.log('finally'))
8、实现 race 方法
race 就是返回最先执行成功的结果。不管是成功还是失败。这样我们只需要遍历该 Promise ,正常返回数据。谁先执行完成,谁先返回即可。注意要控制状态,防止返回多个结果。 race 只需要返回最快的一个结果。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedthis.cbResolveQueue = []this.cbRejectQueue = []callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = resconst run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {cbFn && cbFn(res)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = errconst run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {cbFn && cbFn(err)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {return new LPromise((resolve, reject) => {const cbResolve = res => {const resolveRes = onResolve && onResolve(res)if (resolveRes instanceof LPromise) {resolveRes.then(resolve)} else {resolve(res)}}this.cbResolveQueue.push(cbResolve)const cbReject = err => {onReject && onReject(err)reject(err)}this.cbRejectQueue.push(cbReject)})}catch(err) {this.then(undefined, err)}finally(callback) {this.then(callback, callback)}static resolve(res) {return new LPromise(resolve => resolve(res))}static reject(err) {return new LPromise((undefined, reject) => reject(err))}/* new content start */static race(promiseArr) {return new LPromise((resolve, reject) => {let isContinue = truepromiseArr.forEach(promise => {promise.then(res => {if (isContinue) {isContinue = falseresolve(res)}},err => {if (isContinue) {isContinue = falsereject(err)}})})})}/* new content end */}const p1 = new LPromise((resolve, reject) => setTimeout(() => resolve(1), 200))const p2 = new LPromise((resolve, reject) => setTimeout(() => reject(2), 1000))const p3 = new LPromise((resolve, reject) => setTimeout(() => resolve(3), 3000))LPromise.race([p1, p2, p3]).then(res => console.log('res', res),err => console.log('err', err))
9、实现 all 方法
all 方法当所有 Promise 都成功时返回所有结果的数组,否则返回第一个失败的结果。我们只需要遍历该 Promise 数组。定义个变量存放当前 res 的长度。如果长度等于数组的长度,我们就 resolve 出去。否则发现第一个失败的时候,直接 reject。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedthis.cbResolveQueue = []this.cbRejectQueue = []callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = resconst run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {cbFn && cbFn(res)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = errconst run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {cbFn && cbFn(err)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {return new LPromise((resolve, reject) => {const cbResolve = res => {const resolveRes = onResolve && onResolve(res)if (resolveRes instanceof LPromise) {resolveRes.then(resolve)} else {resolve(res)}}this.cbResolveQueue.push(cbResolve)const cbReject = err => {onReject && onReject(err)reject(err)}this.cbRejectQueue.push(cbReject)})}catch(err) {this.then(undefined, err)}finally(callback) {this.then(callback, callback)}static resolve(res) {return new LPromise(resolve => resolve(res))}static reject(err) {return new LPromise((undefined, reject) => reject(err))}static race(promiseArr) {return new LPromise((resolve, reject) => {let isContinue = truepromiseArr.forEach(promise => {promise.then(res => {if (isContinue) {isContinue = falseresolve(res)}},err => {if (isContinue) {isContinue = falsereject(err)}})})})}/* new content start */static all(promiseArr) {return new LPromise((resolve, reject) => {const resArr = []const length = promiseArr.lengthpromiseArr.forEach(p => {p.then(res => {resArr.push(res)if (resArr.length === length) {resolve(resArr)}},err => reject(err))})})}/* new content end */}const p1 = new LPromise((resolve, reject) => setTimeout(() => resolve(1), 200))const p2 = new LPromise((resolve, reject) => setTimeout(() => reject(2), 1000))const p3 = new LPromise((resolve, reject) => setTimeout(() => reject(3), 3000))LPromise.all([p1, p2, p3]).then(res => console.log('res', res),err => console.log('err', err))
10、实现 allSettled 方法
该方法与 all 类似。只是这个方法不管成功或失败,只要该 Promise 数组执行完毕。就会返回所有结果。我们只需要判断执行过的 Promise 长度是否等于数组长度。当一致时就直接 resolve。并且每次执行的时候,将返回值以指定格式的对象保存到返回的 res 中。
class LPromise {constructor(callbackFn) {this['[[PromiseState]]'] = 'pending'this['[[PromiseResult]]'] = undefinedthis.cbResolveQueue = []this.cbRejectQueue = []callbackFn(this.#resolve.bind(this), this.#reject.bind(this))}#resolve(res) {this['[[PromiseState]]'] = 'fulfilled'this['[[PromiseResult]]'] = resconst run = () => {let cbFnwhile ((cbFn = this.cbResolveQueue.shift())) {cbFn && cbFn(res)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}#reject(err) {this['[[PromiseState]]'] = 'reject'this['[[PromiseResult]]'] = errconst run = () => {let cbFnwhile ((cbFn = this.cbRejectQueue.shift())) {cbFn && cbFn(err)}}const ob = new MutationObserver(run)ob.observe(document.body, { attributes: true })document.body.setAttribute('lpromise', 'layouwen')}then(onResolve, onReject) {return new LPromise((resolve, reject) => {const cbResolve = res => {const resolveRes = onResolve && onResolve(res)if (resolveRes instanceof LPromise) {resolveRes.then(resolve)} else {resolve(res)}}this.cbResolveQueue.push(cbResolve)const cbReject = err => {onReject && onReject(err)reject(err)}this.cbRejectQueue.push(cbReject)})}catch(err) {this.then(undefined, err)}finally(callback) {this.then(callback, callback)}static resolve(res) {return new LPromise(resolve => resolve(res))}static reject(err) {return new LPromise((undefined, reject) => reject(err))}static race(promiseArr) {return new LPromise((resolve, reject) => {let isContinue = truepromiseArr.forEach(promise => {promise.then(res => {if (isContinue) {isContinue = falseresolve(res)}},err => {if (isContinue) {isContinue = falsereject(err)}})})})}static all(promiseArr) {return new LPromise((resolve, reject) => {const resArr = []const length = promiseArr.lengthpromiseArr.forEach(p => {p.then(res => {resArr.push(res)if (resArr.length === length) {resolve(resArr)}},err => reject(err))})})}/* new content start */static allSettled(promiseArr) {return new LPromise(resolve => {const resArr = new Array(promiseArr.length)let num = 0promiseArr.forEach(p => {let obj = {}p.then(res => {obj.status = 'fulfilled'obj.value = resresArr[num] = objnum++if (num === resArr.length) resolve(resArr)},err => {obj.status = 'rejected'obj.reason = errresArr[num] = objnum++if (num === resArr.length) resolve(resArr)})})})}/* new content end */}const p1 = new LPromise((resolve, reject) => setTimeout(() => resolve(1), 200))const p2 = new LPromise((resolve, reject) => setTimeout(() => reject(2), 1000))const p3 = new LPromise((resolve, reject) => setTimeout(() => reject(3), 3000))LPromise.allSettled([p1, p2, p3]).then(res => console.log('res', res),err => console.log('err', err))
End
本文章实现的 Promise 不够完善,只是大概把实现原理带大家过一遍。Promise 的源码不是由 js 写的,所以我们只能尽可能使用 js 模仿。欢淫各位大佬补充更完善的版本。
交流学习~
微信:gdgzyw
github: www.github.com/layouwen
