-
-
Save qetr1ck-op/00e20af1ffee43a1bfc34c6b4a059bf5 to your computer and use it in GitHub Desktop.
Simple Promise implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) | |
| } | |
| }) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment