Last active
April 7, 2019 12:10
-
-
Save acutmore/2a8757c93bdb32b7c935ef19ecf2c5be to your computer and use it in GitHub Desktop.
Promise Implementation where callbacks resolve in random order - To help catch race conditions
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 OriginalPromise = Promise; | |
const enum State { | |
PENDING, | |
RESOLVED, | |
REJECTED | |
} | |
const emptyFn = () => {}; | |
const FUDGE_FACTOR_MS = 100; | |
export class ChaosPromise<T> implements Promise<T> { | |
static all(vs: any[]) { | |
return OriginalPromise.all(vs); | |
} | |
static race(vs: any[]) { | |
return OriginalPromise.race(vs); | |
} | |
static resolve(v: any) { | |
return new ChaosPromise(resolve => resolve(v)); | |
} | |
static reject(v: any) { | |
return new ChaosPromise((_, reject) => reject(v)); | |
} | |
static monkeyPatchPrototype(originalPrototype: {then: Function}): void { | |
const originalThen = originalPrototype.then; | |
originalPrototype.then = function(resolve?: Function, reject?: Function) { | |
const chaosPromise = new ChaosPromise(emptyFn); | |
originalThen.call(this, chaosPromise.resolve, chaosPromise.reject); | |
return chaosPromise.then(resolve, reject); | |
}; | |
} | |
private state = State.PENDING; | |
private value: T = undefined; | |
private callbacks: ([Function, Function, ChaosPromise<T>])[] = []; | |
constructor(revealed: (resolve: Function, reject: Function) => void) { | |
if (typeof revealed !== 'function') { | |
throw new TypeError(`Promise resolver ${typeof revealed} is not a function`); | |
} | |
try { | |
revealed(this.resolve.bind(this), this.reject.bind(this)); | |
} catch (e) { | |
this.reject(e); | |
} | |
} | |
private resolve(v: any): void { | |
this.settle(State.RESOLVED, v); | |
} | |
private reject(e: any): void { | |
this.settle(State.REJECTED, e); | |
} | |
then(onResolve?: Function, onReject?: Function): ChaosPromise<any> { | |
const p = new ChaosPromise<any>(emptyFn); | |
if (this.state === State.PENDING) { | |
this.callbacks.push([onResolve, onReject, p]); | |
} else { | |
this.process(onResolve, onReject, p); | |
} | |
return p; | |
} | |
catch(onReject?: Function): ChaosPromise<any> { | |
return this.then(undefined, onReject); | |
} | |
finally(onSettled?: Function): ChaosPromise<any> { | |
return this.then( | |
(v: any) => { | |
return Promise.resolve(onSettled()).then(_ => v); | |
}, | |
(e: unknown) => { | |
return Promise.resolve(onSettled()).then(_ => { | |
throw e; | |
}); | |
} | |
); | |
} | |
done(onResolve?: Function, onReject?: Function): void { | |
this.then(onResolve, onReject).catch((e: unknown) => { | |
console.error(e); | |
}); | |
} | |
private settle(state: State.RESOLVED | State.REJECTED, v: any): void { | |
if (this.state !== State.PENDING) { | |
return; | |
} | |
const vIsPromise = typeof v === 'object' && v != null && typeof v.then === 'function'; | |
if (state === State.RESOLVED && vIsPromise) { | |
v.then(this.settle.bind(this, State.RESOLVED), this.settle.bind(this, State.REJECTED)); | |
return; | |
} | |
this.state = state; | |
this.value = v; | |
const callbacks = this.callbacks; | |
this.callbacks = []; | |
for (const [re, rj, p] of callbacks) { | |
this.process(re, rj, p); | |
} | |
} | |
private process( | |
onResolve: Function | null, | |
onReject: Function | null, | |
p: ChaosPromise<any> | |
): void { | |
const callback = | |
this.state === State.RESOLVED | |
? onResolve || ((v: any) => v) | |
: onReject || ((e: unknown) => Promise.reject(e)); | |
setTimeout(() => { | |
try { | |
const retVal = callback(this.value); | |
p.resolve(retVal); | |
} catch (e) { | |
p.reject(e); | |
} | |
}, Math.floor(Math.random() * FUDGE_FACTOR_MS)); | |
} | |
} | |
export function monkeyPatchPrototype(originalPrototype: {then: Function}): void { | |
ChaosPromise.monkeyPatchPrototype(originalPrototype); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment