const PENDING = 1 const RESOLVED = 2 const REJECTED = 3 const callLater = setImmediate ? setImmediate // efficient in Node : (fn) => setTimeout(fn, 0) // only choice in browsers class Promise { constructor(fn) { this._state = PENDING this._value = undefined this._thenners = [] this._resolve = this._resolve.bind(this) this._reject = this._reject.bind(this) fn(this._resolve, this._reject) } /* Helper methods */ _resolve(value) { if (this._state === PENDING) { this._state = RESOLVED this._value = value while (this._thenners.length > 0) { this._handleThenner(this._thenners.pop()) } } } _reject(value) { if (this._state === PENDING) { this._state = REJECTED this._value = value while (this._thenners.length > 0) { this._handleThenner(this._thenners.pop()) } } } _handleThenner(thenner) { if (this._state === RESOLVED) { thenner.onResolved && callLater(() => thenner.onResolved(this._value)) } else if (this._state === REJECTED) { thenner.onRejected && callLater(() => thenner.onRejected(this._value)) } else { this._thenners.push(thenner) } } /* Public methods */ then(onResolved, onRejected) { return new Promise((resolve, reject) => { const thenner = { onResolved: (value) => { let nextValue = value if (onResolved) { try { nextValue = onResolved(value) if (nextValue && nextValue.then) { return nextValue.then(resolve, reject) } } catch (err) { return reject(err) } } resolve(nextValue) }, onRejected: (value) => { let nextValue = value if (onRejected) { try { nextValue = onRejected(value) if (nextValue && nextValue.then) { return nextValue.then(resolve, reject) } } catch (err) { return reject(err) } } resolve(nextValue) } } this._handleThenner(thenner) }) } done(onResolved) { return this.then(onResolved) } catch(onRejected) { return this.then(undefined, onRejected) } /* Public static tools */ static resolve(value) { return new Promise((resolve) => resolve(value)) } static reject(value) { return new Promise((resolve, reject) => reject(value)) } static delay(ms, value) { return new Promise((resolve) => setTimeout(() => resolve(value), ms)) } static fromNode(fn) { return new Promise((resolve, reject) => { const resolveNode = (err, res) => { if (err) return reject(err) resolve(res) } fn(resolveNode) }) } static promisify(nodeFn) { return (...args) => Promise.fromNode((resolveFn) => nodeFn(...args, resolveFn)) } static promisifyAll(module) { Object.keys(module) .filter((key) => typeof module[key] === 'function' && !key.endsWith('Sync')) .forEach((key) => module[`${key}Async`] = Promise.promisify(module[key])) return module } static all(promises) { return new Promise((resolve, reject) => { const values = new Array(promises.length) let counter = 0 const tryResolve = (i) => (value) => { values[i] = value counter++ if (counter === promises.length) { resolve(values) } } for (let i = 0; i < promises.length; i++) { const promise = promises[i] promise.then(tryResolve(i), reject) } }) } }