0

I'm using this javascript code for @tanstack/query IndexedDB persistance as documented here.

I'm having too much put() calls and I would like to throttle/debounce them.

How can I do this in the below code?

function createIDBPersister(store: UseStore): Persister {
  return {
    async persistClient(client: PersistedClient) {
      // this is called a lot of times!
      console.log('persistClient()');
      
      set("custom-db", client, store);
    },
    async restoreClient() {
      return await get<PersistedClient>("custom-db", store);
    },
    async removeClient() {
      await del("custom-db", store);
    }
  };
}

I tried with:

function createIDBPersister(store: UseStore): Persister {
  return {
    async persistClient(client: PersistedClient) {
      
      debounce(() => { // import { debounce } from 'lodash-es';
        console.log('persistClient() inside debounce');
        
        set("custom-db", client, store);
      }, 1000)()

    },
    // ...rest
  };
}

But this write the same amount of console.log() messages of the first one, only a second late.

Fred Hors
  • 3,258
  • 3
  • 25
  • 71
  • throttling/retrying does *not* seem like the right pattern for an in-memory browser. In fact, isn't indexeddb synchronous? Unless 'm misunderstanding what you're doing, it feels like maybe you need to take a step back and look at the causes of your issue a step deeper. – Evert Aug 08 '23 at 21:40
  • IndexedDB is not sync. What is going on here is that @tanstack/query is continuously triggering the `persistClient()` after each API call because it needs to write the data to the cache of course. What I need is to debounce that cache writes. Am I wrong? – Fred Hors Aug 08 '23 at 21:48
  • I see, so your goal is then not to postpone calls, but provide a buffer so rapid-fire multiple writes to the same key get de-duplicated/last one wins? And am I assuming correct you are experiencing real performance issues, and not hypothetical/theorized ones? – Evert Aug 08 '23 at 22:01
  • @Evert yes. Real. – Fred Hors Aug 08 '23 at 22:10
  • Does `PersistedClient` have a property that would make it uniquely identifyable? – Evert Aug 08 '23 at 22:14
  • I don't know. Does it matter? – Fred Hors Aug 08 '23 at 22:18
  • Yes, because unless you comfortable with calls to persistClient that never get stored, you need some way to find out what the last version of a given client is. – Evert Aug 08 '23 at 22:19
  • If the requirement is, "I only want 1 call to persistClient per second to succeed" you don't need it though. @buondevid's answer does the job then. – Evert Aug 08 '23 at 22:20
  • It sounds like you want some kind of Promise queue. Alternatively, consider adding a shared promise that you immediately insert between calls. `const delay = () => new Promise(r => setTimeout(r, 500)); let waiter = delay();` then in your functions, `async myFn() { await waiter; await operationToThrottle(); waiter = delay();} ` – Aluan Haddad Aug 08 '23 at 22:31

1 Answers1

0

I don't know about the tanstack, but it looks like it's persistClient() getting called too many times, so you can't debounce inside the function that you want to debounce. You have to debounce the call site of persistClient().

So whatever is calling that function, you can do:

function debounce(func, timeout = 200){
  let timer;

  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => { func.apply(this, args); }, timeout);
  };
}

const debouncedPersistClient = debounce(() => persistClient());

And then use debouncedPersistClient() instead of using directly persistClient().

If you can't control the site, then you could do:

function createIDBPersister(store: UseStore): Persister {
  let timer;
  return {
    async persistClient(client: PersistedClient) {
      clearTimeout(timer)
      timer = setTimeout(() => set("custom-db", client, store), 200);
    },
    // ...
  };
}

Be aware that both with debouncing and throttling you will lose some set calls, I'm not sure that's what you really want.

buondevid
  • 347
  • 2
  • 7