const promiseMap<key, Promise<any>> = new Map();
async function keyedDebounce<R>(key: string, fn: () => R) {
const existingPromise = promiseMap.get(key);
if (existingPromise) return existingPromise;
const promise = fn().then(result => {
promiseMap.delete(key);
return result;
});
promiseMap.set(key, promise);
return promise;
}Could you explain why that's necessary? (sorry probs a stupid question)
A purpose of this might be, let's say you rebuild some structure very frequently, like hundreds of times per second or whatever. You can call this function for a given key as many times as you want while that async process is executing, and it will only execute it once, always returning the same promise.
I’m not entirely sure of the use of this pattern where your using a map and deleting the keys as you go - seems like you’d end up doing more work kinda randomly depending on how the keys were added. I’d just relive the whole map when I was done with it.
OP is intentionally caching results of, presumably, expensive computations or network calls. It's a cache. There is no memory leak. OP just did not detail how cache invalidation/replacement happens. The 2nd comment adds a rather rudimentary mechanism of removing items from cache as soon as they are ready. You get the benefit of batching requests that occur before the resolve, but you get the downside of requests coming in right after the resolve hitting the expensive process again. Requests don't neatly line up at the door and stop coming in as soon as your database query returns.
Typically you would use an LRU mechanism. All caches (memcached, redis, etc.) have a memory limit (whether fixed or unbounded, i.e. all RAM) and a cache replacement policy.
I've mostly used this overall pattern with something like Dataloader, where you throw away the entire cache on every request, so the GC problem doesn't crop up.
Just gotta be careful with memory leaks, so (for a TS example) you might want to do something like this:
So that the promise sticks around for that key while the function is active, but then it clears it so that you're not just building up keys in a map.