💤
javascript
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class ZPromise {
#status = PENDING
#value = undefined
#taskQueue = []
#changeStatus(status, value) {
if (this.#status !== PENDING) return
this.#status = status
this.#value = value
this.#runTaskQueue()
}
constructor(executor) {
const resolve = data => {
// Promise a+ 规范没规定,如果resolve的值是一个thenable,
// 那么这个thenable的状态如何处理,其通篇都是在讲then函数。
// 这里的添加判断,是发现resolve一个thenable对象时,表现与es6的promise不一致。
// 碰到thenable的时候,会直接resolve掉,而非展开。
// prettier-ignore
if (data instanceof ZPromise || (this.#isFnOrObj(data) && typeof data.then === 'function')) {
return data.then(resolve, reject)
}
this.#changeStatus(FULFILLED, data)
}
const reject = reason => {
this.#changeStatus(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
#resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof ZPromise) {
return x.then(y => this.#resolvePromise(promise2, y, resolve, reject), reject)
}
let called = false
if (this.#isFnOrObj(x)) {
try {
const then = x.then
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return
called = true
this.#resolvePromise(promise2, y, resolve, reject)
},
r => {
if (called) return
called = true
reject(r)
}
)
} else {
resolve(x)
}
} catch (e) {
if (called) return
reject(e)
}
} else {
resolve(x)
}
}
#runTask(fn, resolve, reject, promise2) {
this.#runMicroTask(() => {
if (typeof fn !== 'function') {
return this.#status === FULFILLED ? resolve(this.#value) : reject(this.#value)
}
try {
const x = fn(this.#value)
this.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
#runTaskQueue() {
if (this.#status === PENDING) return
while (this.#taskQueue.length) {
const { onFulfilled, onRejected, resolve, reject, promise2 } = this.#taskQueue.shift()
this.#runTask(this.#status === FULFILLED ? onFulfilled : onRejected, resolve, reject, promise2)
}
}
then(onFulfilled, onRejected) {
const promise2 = new ZPromise((resolve, reject) => {
this.#runMicroTask(() => {
this.#taskQueue.push({ onFulfilled, onRejected, resolve, reject, promise2 })
this.#runTaskQueue()
})
})
return promise2
}
// utils
#runMicroTask(fn) {
if (typeof queueMicrotask === 'function') {
queueMicrotask(fn)
return
}
if (typeof MutationObserver === 'function') {
const observer = new MutationObserver(fn)
observer.observe(document.body, { attributes: true })
document.body.setAttribute('kkb', Math.random())
return
}
if (typeof process === 'object' && process.nextTick) {
process.nextTick(fn)
return
}
setTimeout(fn, 0)
}
#isFnOrObj(x) {
return (x && typeof x === 'object') || typeof x === 'function'
}
}