Created
December 22, 2018 04:57
-
-
Save moodysalem/e0445bcad995eb1c4f8d974434410967 to your computer and use it in GitHub Desktop.
TypeScript: Higher order function to prevent duplicate calls to an async function, i.e. memoize
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
import { expect } from 'chai'; | |
import preventDuplicateAsync from '../src/util/prevent-duplicate-async'; | |
function testPromise<T>(): { promise: Promise<T>; resolve: (T) => void; reject: (Error) => void; } { | |
let rslv, rjct; | |
const promise = new Promise<T>((resolve, reject) => { | |
rslv = resolve; | |
rjct = reject; | |
}); | |
return { resolve: rslv, reject: rjct, promise }; | |
} | |
function wait(ms: number): Promise<void> { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
describe('preventDuplicateAsync', () => { | |
it.only('works', async () => { | |
const testFn = preventDuplicateAsync<{ key: string; proxy: Promise<void> }, void>( | |
async ({ key, proxy }) => { | |
await proxy; | |
}, | |
({ key }) => key | |
); | |
const p1 = testPromise<void>(); | |
const p2 = testPromise<void>(); | |
const p3 = testPromise<void>(); | |
const dedupedOne = testFn({ key: 'x', proxy: p1.promise }); | |
const dedupedTwo = testFn({ key: 'x', proxy: p2.promise }); | |
const notDedupedThree = testFn({ key: 'y', proxy: p3.promise }); | |
// It uses the promise cache to prevent duplicate requests | |
expect(dedupedOne).to.eq(dedupedTwo); | |
expect(dedupedTwo).to.not.eq(notDedupedThree); | |
expect(dedupedOne).to.not.eq(notDedupedThree); | |
let oneResolved = false; | |
dedupedOne.then(() => oneResolved = true); | |
let twoResolved = false; | |
dedupedTwo.then(() => twoResolved = true); | |
let threeResolved = false; | |
notDedupedThree.then(() => threeResolved = true); | |
// now if we resolve p2, nothing should happen because the promise isn't used | |
p2.resolve(void 0); | |
await wait(0); | |
expect(oneResolved).to.eq(false, 'one is not resolved after p2'); | |
expect(twoResolved).to.be.eq(false, 'two is not resolved after p2'); | |
expect(threeResolved).to.be.eq(false, 'three is not resolved after p2'); | |
// if we resolve p1, that should work | |
p1.resolve(void 0); | |
await wait(0); | |
expect(oneResolved).to.be.eq(true, 'one is resolved after p1'); | |
expect(twoResolved).to.be.eq(true, 'two is resolved after p1'); | |
expect(threeResolved).to.be.eq(false, 'three is not resolved after p1'); | |
p3.resolve(void 0); | |
await wait(0); | |
expect(oneResolved).to.be.eq(true, 'one is resolved after p3'); | |
expect(twoResolved).to.be.eq(true, 'two is resolved after p3'); | |
expect(threeResolved).to.be.eq(true, 'three is resolved after p3'); | |
}); | |
}); |
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
export default function preventDuplicateAsync<TArgs, TResult>( | |
func: (TArgs) => Promise<TResult>, | |
keyFunction: (TArgs) => string | |
) { | |
let promiseCache: { [ key: string ]: Promise<TResult> } = {}; | |
return function deduped(args: TArgs): Promise<TResult> { | |
const key = keyFunction(args); | |
if (promiseCache[ key ]) { | |
return promiseCache[ key ]; | |
} | |
return (promiseCache[ key ] = func(args) | |
.then(result => { | |
delete promiseCache[ key ]; | |
return result; | |
}) | |
.catch(error => { | |
delete promiseCache[ key ]; | |
throw error; | |
})); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment