Skip to content

Instantly share code, notes, and snippets.

@pie6k
Last active September 18, 2019 07:50
Show Gist options
  • Save pie6k/213b65b4c7ae1dfc09f56da2445b5252 to your computer and use it in GitHub Desktop.
Save pie6k/213b65b4c7ae1dfc09f56da2445b5252 to your computer and use it in GitHub Desktop.
Suspensify Promise Typescript
type AllowedArg = number | string | boolean | null | undefined;
function stringifyArg(arg: AllowedArg) {
return `${arg}`;
}
function serializeArgs(args: AllowedArg[]) {
const argsLimiter = "<<<<***>>>>";
if (args.length === 0) {
return "_";
}
if (args.length === 1) {
return args[0].toString() + argsLimiter;
}
return args.map(stringifyArg).join(argsLimiter);
}
function suspendifyPromise<T, A extends AllowedArg[]>(
promiseGetter: (...args: A) => Promise<T>,
): (...args: A) => T {
// cache that will keep results for provided arguments
const cache: { [argsKey: string]: T | Promise<T> } = {};
return function getSuspended(...args: A) {
// prepare cache key for given args
const argsKey = serializeArgs(args);
const cacheValue = cache[argsKey];
// we have cache! it was called before with the same arguments (but it might be not resolved yet)
if (cacheValue !== undefined) {
// if value in cache is still promise
if (cacheValue instanceof Promise) {
// throw it to suspend
throw cacheValue;
}
// cache value is resolved - just return it
return cacheValue;
}
// get promise, but dont wait for it to resolve
const promise = promiseGetter(...args);
// save promise to cache to avoid creating promise again if it's requested again before it's resolved
cache[argsKey] = promise;
// wait for promise to resolve and then add resolved value to cache
promise.then(result => {
cache[argsKey] = result;
});
// suspense with promise of value
throw promise;
};
}
const getHello = suspendifyPromise(
(id: string) =>
new Promise<string>(resolve => {
resolve(`Hello, ${id}`);
}),
);
// getHello("Bob") -> "Hello, Bob" or throw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment