基本结构的实现
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#status = this.#STATUS_PENDING
#resolve = () => {}
#reject = () => {}
constructor(executor) {
const resolve = value => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_FULFILLED
// 执行到此处的时候,只是在执行executor方法
// 并没有真正执行到then方法
// 所以使用queueMicrotask将resolve或reject函数的执行放入到当前事件队列的微任务队列中
// 让其不阻塞主线程的执行,等到主线程全部执行完毕以后,浏览器会自动去执行当前事件事件队列中的微任务队列
queueMicrotask(() => {
this.#resolve(value)
})
}
}
const reject = reason => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_REJECTED
queueMicrotask(() => {
this.#reject(reason)
})
}
}
executor(resolve, reject)
}
then(onresolved, onrejected) {
this.#resolve = onresolved
this.#reject = onrejected
}
}
// 测试代码
const promise = new CustomPromise((resolve, reject) => {
resolve(123)
reject('error')
})
promise.then(res => console.log(res), err => console.log(err))
实例方法
then方法
多次执行then方法
之前在promise基本结构实现的代码中,我们如果多次调用then方法,后一个then方法会将之前的then方法覆盖
// 因为内部实现是 this.#resolve = onresolved
// 所以多次进行promise.then调用的时候,后一个会将前一个给覆盖掉
promise.then(res => console.log(res), err => console.log(err))
promise.then(res => console.log('res:' + res), err => console.log('err' + err))
所以实际上,我们应该是要数组存储所有传入的onresolve和onreject方法
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#status = this.#STATUS_PENDING
#resolveFns = []
#rejecetFns = []
constructor(executor) {
const resolve = value => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_FULFILLED
queueMicrotask(() => {
this.#resolveFns.forEach(fn => fn(value))
})
}
}
const reject = reason => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_REJECTED
queueMicrotask(() => {
this.#rejecetFns.forEach(fn => fn(reason))
})
}
}
executor(resolve, reject)
}
then(onresolved, onrejected) {
if (onresolved) {
this.#resolveFns.push(onresolved)
}
if (onrejected) {
this.#rejecetFns.push(onrejected)
}
}
}
// 测试代码
const promise = new CustomPromise((resolve, reject) => {
resolve(123)
reject('error')
})
promise.then(res => console.log(res), err => console.log(err))
promise.then(res => console.log('res: ' + res), err => console.log('err: ' + err))
但是此时依旧存在一个问题
promise.then(res => console.log(res), err => console.log(err))
promise.then(res => console.log('res: ' + res), err => console.log('err: ' + err))
setTimeout(() => {
// 这里的then方法并不会被正常回调
// 因为此时executor中的resolve方法依旧被执行完毕了
// 此时仅仅只是向数组中添加需要执行的函数体,但是并不会实际执行
promise.then(res => console.log('setTimeout res: ' + res), err => console.log('setTimeout err: ' + err))
}, 1000)
所以此时需要对代码进行相应的改进
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#status = this.#STATUS_PENDING
#resolveFns = []
#rejecetFns = []
#value = undefined
#reason = undefined
constructor(executor) {
const resolve = value => {
// add microtask
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_FULFILLED
this.#value = value
this.#resolveFns.forEach(fn => fn(value))
}
})
}
const reject = reason => {
// add microtask
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_REJECTED
this.#reason = reason
this.#rejecetFns.forEach(fn => fn(reason))
}
})
}
executor(resolve, reject)
}
then(onresolved, onrejected) {
// 如果当前状态已经是resolved或rejected, 表明状态已经确定,直接执行对应的函数即可
if (onresolved && this.#status === this.#STATUS_FULFILLED) {
onresolved(this.#value)
}
if (onrejected && this.#status === this.#STATUS_REJECTED) {
onrejected(this.#reason)
}
// 如果当前状态是pending,那么所有的resolved或rejected函数需要存放于数组中,于后期进行调用
if (this.#status === this.#STATUS_PENDING) {
if (onresolved) {
this.#resolveFns.push(onresolved)
}
if (onrejected) {
this.#rejecetFns.push(onrejected)
}
}
}
}
// 测试代码
const promise = new CustomPromise((resolve, reject) => {
resolve(123)
reject('error')
})
promise.then(res => console.log(res), err => console.log(err))
promise.then(res => console.log('res: ' + res), err => console.log('err: ' + err))
setTimeout(() => {
promise.then(res => console.log('setTimeout res: ' + res), err => console.log('setTimeout err: ' + err))
}, 1000)
链式调用
promise.then方法返回值应该是一个新的promise,但是之前的代码逻辑then方法没有返回值,所以是无法实现链式调用的
对此需要进行相应的修改
// tool function ---> 对promise实例的try ... catch ... 方法的封装
function catchPromiseError(fn, param, resolve, reject) {
try {
resolve(fn(param))
} catch (err) {
reject(err)
}
}
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#resolveFns = []
#rejecetFns = []
#value = undefined
#reason = undefined
constructor(executor) {
this.status = this.#STATUS_PENDING
const resolve = value => {
// add microtask
queueMicrotask(() => {
if (this.status === this.#STATUS_PENDING) {
this.status = this.#STATUS_FULFILLED
this.#value = value
this.#resolveFns.forEach(fn => fn(value))
}
})
}
const reject = reason => {
// add microtask
queueMicrotask(() => {
if (this.status === this.#STATUS_PENDING) {
this.status = this.#STATUS_REJECTED
this.#reason = reason
this.#rejecetFns.forEach(fn => fn(reason))
}
})
}
executor(resolve, reject)
}
then(onresolved, onrejected) {
// 返回值需要一个promise, 而promise的executor函数会立即执行
// 所以直接使用一个新的promise包裹then方法中的逻辑实现
return new CustomPromise((resolve, reject) => {
if (onresolved && this.status === this.#STATUS_FULFILLED) {
catchPromiseError(onresolved, this.#value, resolve, reject)
}
if (onrejected && this.status === this.#STATUS_REJECTED) {
catchPromiseError(onrejected, this.#reason, resolve, reject)
}
if (this.status === this.#STATUS_PENDING) {
if (onresolved) {
// 使用闭包
// 传入的是外层的函数,但实际执行的是内部的catchPromiseError方法
// 目的是为了获取正确的resolve和reject方法的值
this.#resolveFns.push(value => catchPromiseError(onresolved, value, resolve, reject))
}
if (onrejected) {
this.#rejecetFns.push(reason => catchPromiseError(onrejected, reason, resolve, reject))
}
}
})
}
}
// 测试代码
const promise = new CustomPromise((resolve, reject) => {
// resolve(123)
reject('error')
})
promise
.then(res => {
console.log('res1: ' + res)
// return 'res1 resolved return value'
throw new Error('error in res1 resolved')
}, err => {
console.log('err1: ' + err)
return 'errr1 rejected return value'
})
.then(res => console.log('res2: ' + res), err => console.log('err2: ' + err))
setTimeout(() => {
promise.then(res => console.log('setTimeout res: ' + res), err => console.log('setTimeout err: ' + err))
}, 1000)
但是此时可能返回值是一个新的promise实例,也可能是一个实现了thenable的对象
function catchPromiseError(fn, param, resolve, reject, status) {
try {
const res = fn(param)
// 如果外层promise的状态为resolved的时候
if (status === 'resolved') {
// 返回值是一个新的promise
if (res instanceof CustomPromise) {
// 执行返回的promise, 以确定实际返回值的状态
res.then(res => resolve(res), err => reject(err))
} else if (typeof res?.then === 'function') {
// 如果返回值是一个实现了then方法的对象, 最终状态由thenable对象决定
res.then(resolve, reject)
} else {
resolve(res)
}
} else {
resolve(res)
}
} catch (err) {
reject(err)
}
}
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#status = this.#STATUS_PENDING
#resolveFns = []
#rejecetFns = []
#value = undefined
#reason = undefined
constructor(executor) {
const resolve = value => {
// add microtask
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_FULFILLED
this.#value = value
this.#resolveFns.forEach(fn => fn(value))
}
})
}
const reject = reason => {
// add microtask
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_REJECTED
this.#reason = reason
this.#rejecetFns.forEach(fn => fn(reason))
}
})
}
executor(resolve, reject)
}
then(onresolved, onrejected) {
return new CustomPromise((resolve, reject) => {
if (onresolved && this.#status === this.#STATUS_FULFILLED) {
catchPromiseError(onresolved, this.#value, resolve, reject, 'resolved')
}
if (onrejected && this.#status === this.#STATUS_REJECTED) {
catchPromiseError(onrejected, this.#reason, resolve, reject)
}
if (this.#status === this.#STATUS_PENDING) {
if (onresolved) {
this.#resolveFns.push(value => catchPromiseError(onresolved, value, resolve, reject, 'resolved'))
}
if (onrejected) {
this.#rejecetFns.push(reason => catchPromiseError(onrejected, reason, resolve, reject))
}
}
})
}
}
// 测试代码
const promise = new CustomPromise((resolve, reject) => {
resolve(123)
// reject('error')
})
promise
.then(res => {
console.log('res1: ' + res)
// return new CustomPromise((resolve, reject) => reject('something error in return promise'))
return {
then(resolve, reject) {
reject('rejected in thenable object')
}
}
}, err => {
console.log('err1: ' + err)
})
.then(res => console.log('res2: ' + res), err => console.log('err2: ' + err))
catch方法
function catchPromiseError(fn, param, resolve, reject, status) {
try {
const res = fn(param)
if (status === 'resolved') {
if (res instanceof CustomPromise) {
res.then(res => resolve(res), err => reject(err))
} else if (typeof res?.then === 'function') {
res.then(resolve, reject)
} else {
resolve(res)
}
} else {
resolve(res)
}
} catch (err) {
reject(err)
}
}
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#status = this.#STATUS_PENDING
#resolveFns = []
#rejecetFns = []
#value = undefined
#reason = undefined
constructor(executor) {
const resolve = value => {
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_FULFILLED
this.#value = value
this.#resolveFns.forEach(fn => fn(value))
}
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_REJECTED
this.#reason = reason
this.#rejecetFns.forEach(fn => fn(reason))
}
})
}
executor(resolve, reject)
}
// 如果没有传入onrejected方法,那么就使用默认值
// 如果then自身没有处理异常,就交给下一个then方法或catch方法进行处理
then(onresolved, onrejected = err => { throw err }) {
return new CustomPromise((resolve, reject) => {
if (onresolved && this.#status === this.#STATUS_FULFILLED) {
catchPromiseError(onresolved, this.#value, resolve, reject, 'resolved')
}
if (onrejected && this.#status === this.#STATUS_REJECTED) {
catchPromiseError(onrejected, this.#reason, resolve, reject)
}
if (this.#status === this.#STATUS_PENDING) {
if (onresolved) {
this.#resolveFns.push(value => catchPromiseError(onresolved, value, resolve, reject, 'resolved'))
}
if (onrejected) {
this.#rejecetFns.push(reason => catchPromiseError(onrejected, reason, resolve, reject))
}
}
})
}
catch(onrejected) {
// catch本质上就是一个特殊的 resolved函数的值为undefined的 then方法
// catch方法依旧需要返回一个新的promise实例
return this.then(undefined, onrejected)
}
}
// 测试代码
const promise = new CustomPromise((resolve, reject) => {
reject('error')
})
promise
.then(res => {
console.log('res1: ' + res)
})
// 这里的catch本质上接收的应该是promise.then方法返回的promise的rejected方法
// 而不是promise返回的rejected方法
.catch(err => console.log('err2: ' + err))
// 当promise自身可以处理状态为rejected的时候, 就不需要catch方法来处理对应逻辑了
promise
.then(res => {
console.log('res1: ' + res)
}, err => console.log('err1: ' + err))
.catch(err => console.log('err2: ' + err))
finally方法
function catchPromiseError(fn, param, resolve, reject, status) {
try {
const res = fn(param)
if (status === 'resolved') {
if (res instanceof CustomPromise) {
res.then(res => resolve(res), err => reject(err))
} else if (typeof res?.then === 'function') {
res.then(resolve, reject)
} else {
resolve(res)
}
} else {
resolve(res)
}
} catch (err) {
reject(err)
}
}
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#status = this.#STATUS_PENDING
#resolveFns = []
#rejecetFns = []
#value = undefined
#reason = undefined
constructor(executor) {
const resolve = value => {
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_FULFILLED
this.#value = value
this.#resolveFns.forEach(fn => fn(value))
}
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_REJECTED
this.#reason = reason
this.#rejecetFns.forEach(fn => fn(reason))
}
})
}
executor(resolve, reject)
}
// 因为catch之后依旧可以使用then方法,而catch是不会处理resolve状态的
// 所以需要将上一个promise返回的resolve状态的值进行转发
// 交给下一个then方法来进行处理
then(onresolved = value => value, onrejected = err => { throw err }) {
return new CustomPromise((resolve, reject) => {
if (this.#status === this.#STATUS_FULFILLED) {
catchPromiseError(onresolved, this.#value, resolve, reject, 'resolved')
}
if (this.#status === this.#STATUS_REJECTED) {
catchPromiseError(onrejected, this.#reason, resolve, reject)
}
if (this.#status === this.#STATUS_PENDING) {
this.#resolveFns.push(value => catchPromiseError(onresolved, value, resolve, reject, 'resolved'))
this.#rejecetFns.push(reason => catchPromiseError(onrejected, reason, resolve, reject))
}
})
}
catch(onrejected) {
return this.then(undefined, onrejected)
}
finally(onfinally) {
// finally不需要在有任何的返回值
// finally也是一个特殊的then方法 --- resolve方法和rejeced方法的实现逻辑是一致的
this.then(onfinally, onfinally)
}
}
// 测试代码
const promise = new CustomPromise((resolve, reject) => {
resolve(123)
// reject('error')
})
promise
.catch(err => {
console.log('err2: ' + err)
return 123
})
.then(res => console.log('res: ' + res))
.finally(() => console.log('finally'))
静态方法
resolve 和 reject
{
// ... 此处进行代码省略
static resolve(value) {
return new CustomPromise(resolve => resolve(value))
}
static reject(reason) {
return new CustomPromise((resolve, reject) => reject(reason))
}
// ... 此处进行代码省略
}
all 和 allSettled
all
static all(promises) {
let count = 0
const results = []
return new CustomPromise((resolve, reject) => {
// 使用IIFE,是为了保证返回的resolve中的数组的顺序和传入的顺序保持一致
// 而不是和执行的先后顺序保持一致
promises.forEach((promise, index) => {
(function(index) {
// 如果传入的不是Promise实例,需要转换为promise实例对象
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
promise.then(res => {
results[index] = res
count++
if (count === promises.length) {
resolve(results)
}
}, reject) // 这里的reject等价于err => reject(err)
})(index)
})
})
}
allSettled
// allSettled方法会等到所有的promise都有结果后,才会返回结果
// allSettled方法 只会执行resolved方法,永远不会执行rejected方法
static allSettled(promises) {
const results = []
let count = 0
return new CustomPromise(resolve => {
promises.forEach((promise, index) => {
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
(function(index) {
promise.then(res => {
results[index] = {
status: 'fulfilled',
value: res
}
count++
if(count === promises.length){
resolve(results)
}
}, err => {
results[index] = {
stauts: 'rejected',
reason: err
}
count++
if(count === promises.length){
resolve(results)
}
})
})(index)
})
})
}
race 和 any
race
static race(promises) {
return new CustomPromise((resolve, reject) => {
promises.forEach(promise => {
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
promise.then(resolve, reject)
})
})
}
any
static any(promises) {
const results = []
let count = 0
return new CustomPromise((resolve, reject) => {
promises.forEach((promise, index) => {
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
(function(index) {
promise.then(resolve, err => {
results[index] = err
count++
if (count === promises.length) {
// 目前node的LTS版本是 v14.18.1
// AggregateError是ES2021提出的新内容
// 所以any方法需要在current版本的node中或最新版本的浏览器中进行测试
reject(new AggregateError(results, 'All promises were rejected'))
}
})
})(index)
})
})
}
完整实现
function catchPromiseError(fn, param, resolve, reject, status) {
try {
const res = fn(param)
if (status === 'resolved') {
if (res instanceof CustomPromise) {
res.then(res => resolve(res), err => reject(err))
} else if (typeof res?.then === 'function') {
res.then(resolve, reject)
}
} else {
resolve(res)
} else {
resolve(res)
}
} catch (err) {
reject(err)
}
}
class CustomPromise {
#STATUS_PENDING = 'pending'
#STATUS_FULFILLED = 'fulfilled'
#STATUS_REJECTED = 'rejected'
#status = this.#STATUS_PENDING
#resolveFns = []
#rejecetFns = []
#value = undefined
#reason = undefined
constructor(executor) {
const resolve = value => {
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_FULFILLED
this.#value = value
this.#resolveFns.forEach(fn => fn(value))
}
})
}
const reject = reason => {
queueMicrotask(() => {
if (this.#status === this.#STATUS_PENDING) {
this.#status = this.#STATUS_REJECTED
this.#reason = reason
this.#rejecetFns.forEach(fn => fn(reason))
}
})
}
executor(resolve, reject)
}
then(onresolved = value => value, onrejected = err => { throw err }) {
return new CustomPromise((resolve, reject) => {
if (this.#status === this.#STATUS_FULFILLED) {
catchPromiseError(onresolved, this.#value, resolve, reject, 'resolved')
}
if (this.#status === this.#STATUS_REJECTED) {
catchPromiseError(onrejected, this.#reason, resolve, reject)
}
if (this.#status === this.#STATUS_PENDING) {
this.#resolveFns.push(value => catchPromiseError(onresolved, value, resolve, reject, 'resolved'))
this.#rejecetFns.push(reason => catchPromiseError(onrejected, reason, resolve, reject))
}
})
}
catch(onrejected) {
return this.then(undefined, onrejected)
}
finally(onfinally) {
this.then(onfinally, onfinally)
}
static resolve(value) {
return new CustomPromise(resolve => resolve(value))
}
static reject(reason) {
return new CustomPromise((resolve, reject) => reject(reason))
}
static all(promises) {
let count = 0
const results = []
return new CustomPromise((resolve, reject) => {
promises.forEach((promise, index) => {
(function(index) {
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
promise.then(res => {
results[index] = res
count++
if (count === promises.length) {
resolve(results)
}
}, reject)
})(index)
})
})
}
static allSettled(promises) {
const results = []
let count = 0
return new CustomPromise(resolve => {
promises.forEach((promise, index) => {
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
(function(index) {
promise.then(res => {
results[index] = {
status: 'fulfilled',
value: res
}
count++
if(count === promises.length){
resolve(results)
}
}, err => {
results[index] = {
stauts: 'rejected',
reason: err
}
count++
if(count === promises.length){
resolve(results)
}
})
})(index)
})
})
}
static race(promises) {
return new CustomPromise((resolve, reject) => {
promises.forEach(promise => {
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
promise.then(resolve, reject)
})
})
}
static any(promises) {
const results = []
let count = 0
return new CustomPromise((resolve, reject) => {
promises.forEach((promise, index) => {
if (!(promise instanceof CustomPromise)) {
promise = CustomPromise.resolve(promise)
}
(function(index) {
promise.then(resolve, err => {
results[index] = err
count++
if (count === promises.length) {
reject(new AggregateError(results, 'All promises were rejected'))
}
})
})(index)
})
})
}
}