附上一份自以为比较完整的 promise 实现代码,可根据 promise/A+ 规范自行测试,大部分代码源于方应杭老师(可参考:视频课程)的教程,其中做了一些优化调整,若道友发现有何不妥之处,欢迎留言讨论,期待能实现得更加完美!
class Promise2 {
#state = 'pending'
#callbacks = []
#outerData = undefined
constructor(fn) {
if (typeof fn !== 'function') {
throw new Error('必须传入一个函数')
}
fn(this.#resolve.bind(this), this.#reject.bind(this))
}
#executeCallbacks() {
if (this.#state === 'pending') return
nextTick(() => {
this.#callbacks.forEach(handle => {
const hash = {
fulfilled: 0,
rejected: 1
}
const specificIndex = hash[this.#state]
if (typeof handle[specificIndex] === 'function') {
let x
try {
x = handle[specificIndex].call(undefined, this.#outerData)
} catch (error) {
return handle[2].reject(error)
}
handle[2].#resolveWith(x)
} else {
handle[2].#resolveWith()
}
})
})
}
#resolve(result) {
if (this.#state !== 'pending') {
return
}
this.#state = 'fulfilled'
this.#outerData = result
this.#executeCallbacks()
}
#reject(reason) {
if (this.#state !== 'pending') {
return
}
this.#state = 'rejected'
this.#outerData = reason
this.#executeCallbacks()
}
then(succeed, fail) {
const tempArr = []
if (typeof succeed === 'function') {
tempArr[0] = succeed
}
if (typeof fail === 'function') {
tempArr[1] = fail
}
tempArr[2] = new Promise2(() => { })
this.#callbacks.push(tempArr)
this.#executeCallbacks()
return tempArr[2]
}
// 这里的 this 是在 then 里面返回的handle[2],传的是空函数,没有任何状态,因此这里要把 x 的状态传给 this
#resolveWith(x) {
if (this === x) {
throw new Error('Chaining cycle detected for promise')
} else if (x instanceof Promise2) {
x.then(result => {
this.#resolve(result)
}, reason => {
this.#reject(reason)
})
} else if (x instanceof Object) {
const then = x.then
if (typeof then === 'function') {
try {
then(result => {
this.#resolve(result)
}, reason => {
this.#reject(reason)
})
} catch (error) {
this.#reject(error)
}
} else {
this.#resolve(x)
}
} else {
this.#resolve(x)
}
}
}
// 微任务实现,可应用于浏览器和node环境
function nextTick(fn) {
if (typeof window === 'undefined' && typeof process.nextTick === 'function') {
return process.nextTick(fn)
} else {
const observer = new MutationObserver(fn)
let counter = 1
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
counter += 2
textNode.data = String(counter)
}
}