Skip to content

Instantly share code, notes, and snippets.

@qetr1ck-op
Forked from ronkot/rastas-promise.js
Last active July 16, 2018 20:08
Show Gist options
  • Select an option

  • Save qetr1ck-op/00e20af1ffee43a1bfc34c6b4a059bf5 to your computer and use it in GitHub Desktop.

Select an option

Save qetr1ck-op/00e20af1ffee43a1bfc34c6b4a059bf5 to your computer and use it in GitHub Desktop.
Simple Promise implementation
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