const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED';
const REJECTED = 'REJECTED';
const resolvePromise = function (promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(TypeError('chaning cycle deteced for promise #<Promise>'))
}
let called = false
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (called) return
called = true
reject(e)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reject = undefined
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = []
let resolve = (value) => {
if (value instanceof Promise) {
value.then(resolve, reject)
}
if (status === PENDING) {
this.status = RESOLVED
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = (reject) => {
if (status === PENDING) {
this.status = REJECTED
this.reject = reject
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
this.status = REJECTED
this.reject = e
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : e => e
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
let promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reject)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
} else if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reject)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
catch(errCallback) {
return this.then(null, errCallback)
}
static resolve(data) {
return new Promise((resolve, reject) => {
resolve(data)
})
}
static reject(reson) {
return new Promise((resolve, reject) => {
reject
})
}
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
Promise.prototype.finally = function (callback) {
return this.then((value) => {
return Promise.resolve(callback()).then(() => value)
}, (err) => {
return Promise.resolve(callback()).then(() => { throw err })
})
}
const isPromise = function (value) {
return typeof value.then === 'function'
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
const arr = []
let index = 0
const processData = (key, data) => {
arr[key] = data
index++
if (index === promises.length) {
resolve(arr)
}
}
for (let i = 0; i < promises.length; i++) {
let result = promises[i]
if (isPromise(result)) {
result.then(data => {
processData(i, data)
}, reject)
} else {
processData(i, result)
}
}
})
}
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let result = promises[i]
if (isPromise(result)) {
result.then(resolve, reject)
} else {
resolve(result)
}
}
})
}
module.exports = Promise