一個實現(xiàn)了Promise A+
規(guī)范的promise
采用promises-aplus-tests
包測試通過
測試過程中報過很多錯,本來以為是對于規(guī)范的實現(xiàn)有問題焰檩,后來發(fā)現(xiàn)是代碼在運行過程中報錯導致測試通不過
之后用vscode調(diào)試了代碼后就很容易發(fā)現(xiàn)問題憔涉,所以推薦把promises-aplus-tests
安裝在項目下订框,方便調(diào)試
問題如下
- 其中一個問題是
x.constructor === promise.constructor
判斷時析苫,沒有考慮x
為undefined
時會報錯 - 之前把
resolve
和reject
方法定義在constructor
下時,在then
方法里創(chuàng)建新promise
實例,然后需要根據(jù)不同情況來改變子promise
狀態(tài)衩侥。此時想要把這塊邏輯封裝起來時會發(fā)現(xiàn)国旷,子promise
是拿不到的,因為還未完成變量賦值茫死,除非用setTimeout
把代碼改成異步
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const PROMISE_ID = Math.random().toString(36).substring(2)
function isObjectOrFunction(x) {
let type = typeof x
return x !== null && (type === 'object' || type === 'function')
}
function isFunction(x) {
return typeof x === 'function'
}
let id = 0
function nextId() {
return id++
}
const resolvePromise = (promise, x) => {
if (promise === x) {
return reject(promise, new TypeError('Chaining cycle'))
}
if (x && x.constructor === promise.constructor) {
// 兩種寫法 x instanceof MyPromise 和 x.constructor === promise.constructor跪但;需要注意的是:
// 后一種必須判斷x是否存在 否則會報constructor of undefined的錯誤
x.then((v) => {
resolvePromise(promise, v)
}, (e) => {
reject(promise, e)
})
} else if (isObjectOrFunction(x)) {
// 函數(shù)或?qū)ο? let used = false
let then = void 0
try {
then = x.then
} catch (e) {
return reject(promise, e)
}
if (isFunction(then)) {
try {
then.call(x, (v) => {
if (used) return
used = true
resolvePromise(promise, v)
}, (r) => {
if (used) return
used = true
reject(promise, r)
})
} catch (e) {
if (used) return
used = true
reject(promise, e)
}
} else {
resolve(promise, x)
}
} else {
// 普通值
resolve(promise, x)
}
}
function noop() {}
function flushSchedule(func) {
setTimeout(func, 0)
}
function settlePromise(promise, callback, arg) {
return () => {
let x = void 0
try {
x = callback(arg)
} catch (e) {
return reject(promise, e)
}
resolvePromise(promise, x)
}
}
function resolve (promise, value) {
if (promise.state === PENDING) {
promise.state = FULFILLED
promise.value = value
promise.onFulfilledCallbacks.forEach(fn => fn())
}
}
function reject (promise, reason) {
if (promise.state === PENDING) {
promise.state = REJECTED
promise.reason = reason
promise.onRejectedCallbacks.forEach(fn => fn())
}
}
class MyPromise {
state = PENDING
value = undefined
reason = undefined
onFulfilledCallbacks = []
onRejectedCallbacks = []
constructor(executor) {
this[PROMISE_ID] = nextId()
try {
executor((value) => {
resolve(this, value);
}, (reason) => {
reject(this, reason)
})
} catch (e) {
reject(this, e)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
let child = new this.constructor(noop)
if (this.state === FULFILLED) {
flushSchedule(settlePromise(child, onFulfilled, this.value))
} else if (this.state === REJECTED) {
flushSchedule(settlePromise(child, onRejected, this.reason))
} else if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
flushSchedule(settlePromise(child, onFulfilled, this.value))
})
this.onRejectedCallbacks.push(() => {
flushSchedule(settlePromise(child, onRejected, this.reason))
})
}
return child
}
}
// promises-aplus-tests測試MyPromise的測試代碼
MyPromise.defer = MyPromise.deferred = function () {
let dfd = {}
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = MyPromise