PromiseA+完全版(全篇采用es6语法;node.js环境,node版本需支持类中static申明)
class MyPromise {
// 三种状态
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
static resolve = function (value) {
if (value instanceof MyPromise) {
return value
}
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
static reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
static all = function (promises) {
return new MyPromise((resolve, reject) => {
if (promises === undefined || promises === null || !promises[Symbol.iterator]) {
const errorReason = `${promises === undefined ? '' : typeof promises} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`
reject(new TypeError(errorReason))
return
}
promises = Array.from(promises)
if (promises.length === 0) {
resolve([])
}
let index = 0, valueArr = []
function pushValue(i, value) {
valueArr[i] = value
index++
if (index === promises.length) {
resolve(valueArr)
}
}
for (let i = 0; i < promises.length; i++) {
MyPromise.resolve(promises[i]).then(x => {
pushValue(i, x)
}).catch(e => {
reject(e)
})
}
})
}
static race = function (promises) {
return new MyPromise((resolve, reject) => {
if (promises === null || promises === undefined || !promises[Symbol.iterator]) {
const errorReason = `${promises === undefined ? '' : typeof promises} ${promises} is not iterable (cannot read property Symbol(Symbol.iterator))`
reject(new TypeError(errorReason))
return
}
promises = Array.from(promises)
if (promises.length === 0) return
for (const promise of promises) {
MyPromise.resolve(promise).then(value => resolve(value), reason => reject(reason))
}
})
}
static __Resolve__ = function (promise2, x) {
if (x === promise2) {
promise2.reject(new TypeError('The promise and its value refer to the same object'))
return
} else {
if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {
let time = 0
try {
const then = x.then // 报错点1:访问 then 属性报错
if (typeof then === 'function') {
then.call(x, (y) => { // 报错点2:then 运行报错
if (time++ === 0) {
MyPromise.__Resolve__(promise2, y)
}
}, (r) => {
if (time++ === 0) {
promise2.reject(r)
}
})
} else {
promise2.fulfill(x)
}
} catch (e) {
if (time === 0) {
promise2.reject(e)
}
}
} else {
promise2.fulfill(x)
return;
}
}
}
constructor (executor) {
this.status = MyPromise.PENDING
this.onFulfilleds = []
this.onRejecteds = []
// 根据传入的 value 解决当前环境下的 promise(this)
const resolve = (value) => {
MyPromise.__Resolve__(this, value)
}
// 根据传入的 reason 拒绝当前环境下的 promise(this)
const reject = (reason) => {
this.reject(reason)
}
try {
executor(resolve, reject)
} catch (e) {
this.reject(e)
}
}
// 直接用 value 完成 promise,因为该函数传入的 value 一定不是一个 thenable
fulfill (value) {
if (this.status === MyPromise.PENDING) {
// 修改状态为成功态,此后状态不能再更改
Object.defineProperty(this, 'status', {
value: MyPromise.FULFILLED,
writable: false
})
// 定义属性 value,value 不能再更改
Object.defineProperty(this, 'value', {
value,
writable: false
})
for (const onFulfilled of this.onFulfilleds) {
onFulfilled(this.value)
}
}
}
// 拒绝 promise
reject (reason) {
if (this.status === MyPromise.PENDING) {
// 修改状态为失败态,此后状态不能再更改
Object.defineProperty(this, 'status', {
value: MyPromise.REJECTED,
writable: false
})
Object.defineProperty(this, 'reason', {
value: reason,
writable: false
})
for (const onRejected of this.onRejecteds) {
onRejected(this.reason)
}
}
}
then (onFulfilled, onRejected) {
const promise2 = new MyPromise((resolve, reject) => {
if (typeof onFulfilled !== 'function') {
onFulfilled = function (value) {
return value
}
}
if (typeof onRejected !== 'function') {
onRejected = function (reason) {
throw reason
}
}
switch (this.status) {
case MyPromise.PENDING:
this.onFulfilleds.push(() => {
process.nextTick(() => {
try {
const x = onFulfilled(this.value)
resolve(x)
} catch (e) {
reject(e)
}
})
})
this.onRejecteds.push(() => {
process.nextTick(() => {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (e) {
reject(e)
}
})
})
break
case MyPromise.FULFILLED:
process.nextTick(() => {
try {
const x = onFulfilled(this.value)
resolve(x)
} catch (e) {
reject(e)
}
})
break
case MyPromise.REJECTED:
process.nextTick(() => {
try {
const x = onRejected(this.reason)
resolve(x)
} catch (e) {
reject(e)
}
})
break
}
})
return promise2
}
finally (onFinally) {
return this.then((value) => {
return MyPromise.resolve(onFinally()).then(() => value)
}, (reason) => {
return MyPromise.resolve(onFinally()).then(() => { throw reason })
})
}
catch (onRejected) {
return this.then(undefined, onRejected)
}
}
MyPromise.deferred = function () {
let dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = MyPromise