// https://promisesaplus.com
const PENDING = 0
const FULFILLED = 1
const REJECTED = 2
class Promise {
constructor (executor) {
this._deferreds = []
this._state = PENDING
this._value = null
this._reason = null
executor(value => { this._resolve(value) },
reason => { this._reject(reason) })
}
_handle (deferred) {
if (this._state === PENDING) {
this._deferreds.push(deferred)
} else {
if (typeof(this._state === FULFILLED
? deferred.onFulfilled : deferred.onRejected) === 'function') {
try {
// https://promisesaplus.com/#point-41
deferred.resolve(this._state === FULFILLED
? deferred.onFulfilled(this._value)
: deferred.onRejected(this._reason))
} catch (e) {
// https://promisesaplus.com/#point-42
deferred.reject(e)
}
} else {
// https://promisesaplus.com/#point-43 & https://promisesaplus.com/#point-44
this._state === FULFILLED
? deferred.resolve(this._value) : deferred.reject(this._reason)
}
}
}
_resolve (value) {
// https://promisesaplus.com/#point-17
if (this._state !== PENDING) return
if (value && value.constructor === this.constructor) {
// https://promisesaplus.com/#point-49
value.then(value => { this._resolve(value) },
reason => { this._reject(reason) })
} else {
this._state = FULFILLED
this._value = value
this._done()
}
}
_reject (reason) {
// https://promisesaplus.com/#point-14
if (this._state !== PENDING) return
this._state = REJECTED
this._reason = reason
this._done()
}
_done () {
// console.log(this._state, this._reason || this._value, this.deffereds)
window.setTimeout(_ => {
this._deferreds.forEach(deferred => { this._handle(deferred) })
}, 0)
}
then (onFulfilled, onRejected) {
// https://promisesaplus.com/#point-40
return new this.constructor(
(resolve, reject) => {
this._handle({
onFulfilled: onFulfilled,
onRejected: onRejected,
resolve: resolve,
reject: reject
})
})
}
catch (onRejected) {
return this.then(undefined, onRejected)
}
finally (cb) {
if (typeof cb === 'function') {
return this.then(value => Promise.resolve(cb()).then(_ => value),
reason => Promise.resolve(cb()).then(_ => { throw reason }))
}
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
static resolve (value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject
static reject (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
static race (iterable) {
return new Promise((resolve, reject) => {
iterable.forEach(value => Promise.resolve(value).then(resolve, reject))
})
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
static all (iterable) {
return new Promise((resolve, reject) => {
let values = []
for (let i = 0, len = iterable.length; i < len; ++i) {
iterable[i].then(value => {
values.push(value)
i >= len - 1 && resolve(values)
}, reason => {
reject(reason)
})
}
})
}
}