-
-
Save qetr1ck-op/00e20af1ffee43a1bfc34c6b4a059bf5 to your computer and use it in GitHub Desktop.
Simple Promise implementation
This file contains 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 = fn => setTimeout(fn, 0); | |
class MyPromise { | |
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 MyPromise((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 MyPromise(resolve => resolve(value)); | |
} | |
static reject(value) { | |
return new MyPromise((resolve, reject) => reject(value)); | |
} | |
static delay(ms, value) { | |
return new MyPromise(resolve => setTimeout(() => resolve(value), ms)); | |
} | |
static fromNode(fn) { | |
return new MyPromise((resolve, reject) => { | |
const resolveNode = (err, res) => { | |
if (err) return reject(err); | |
resolve(res); | |
}; | |
fn(resolveNode); | |
}); | |
} | |
static promisify(nodeFn) { | |
return (...args) => MyPromise.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`] = MyPromise.promisify(module[key]))); | |
return module; | |
} | |
static all(Mypromises) { | |
return new MyPromise((resolve, reject) => { | |
const values = new Array(Mypromises.length); | |
let counter = 0; | |
const tryResolve = i => value => { | |
values[i] = value; | |
counter++; | |
if (counter === Mypromises.length) { | |
resolve(values); | |
} | |
}; | |
for (let i = 0; i < Mypromises.length; i++) { | |
const Mypromise = Mypromises[i]; | |
Mypromise.then(tryResolve(i), reject); | |
} | |
}); | |
} | |
} | |
const main = async arguments => { | |
const p = await new MyPromise(resolve => { | |
setTimeout(() => { | |
resolve('data'); | |
}, 100); | |
}); | |
console.log(p); | |
}; | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment