4

NOTE: Caching JavaScript promise results does not apply because that question doesn't involve an API that calls the listener callback on state mutation (which means the API stores the listener function and may call it multiple times, whenever its state gets updated).

I have a state object type State.

Now I have an API API.addEventListener(), which accepts a listener of type function(!State):undefined, and a boolean repeating. If called, this API will store the listener callback, and call it with a State object if

  • a State becomes available (nonexistence to existence)
  • a new State object becomes available (mutation), if repeating is true.

Originally, whenever I call doStuff() to perform my logic, I will call API.addEventListener() to fetch the most recent State object:

/**
 * @return {!Promise{!State}}
 */
function getStatePromise() {
  return new Promise(resolve => {
    API.addEventListener(resolve, /* repeating */ false);
  });
}

function doStuff() {
  getStatePromise().then(state => {
    // my logic
  });
}

doStuff() might be called multiple times at different points in my program.

Now, I'd like to cache the result retrieved from API.getState() in a persistent object in my JS script, and let the code update the cache when a new State becomes available. I don't want to change doStuff(), because it's fairly complicated. Is it possible to just change getStatePromise() to do this?

My current attempt is:

let cacheStorage = {}; // or window.localStorage, but it's beside the point

/**
 * @return {!Promise{!State}}
 */
function getStatePromise() {
  if (cacheStorage.state !== undefined) {
    return Promise.resolve(cacheStorage.state);
  }

  return new Promise(resolve => {
    API.addEventListener(newState => {
        cacheStorage.state = newState;
        resolve(newState); // ??? what if the listener is called again?
    }, /* repeating */ true);
  });
}

function doStuff() {
  getStatePromise().then(state => {
    // my logic
  });
}

When doStuff() is called for the first time, cacheStorage is empty, so getStatePromise() will fetch the State object, populate the cache, and return a promise which resolves to the State object. Then, when doStuff() is called for the second time, it will just return a promise that resolves to the cached State object.

However, the problem is, when a new State becomes available later, API will call the listener callback again, which means resolve() will once again be called (see ??? in code). This doesn't sound right, because it is resolving a promise that is already resolved. How should I fix this?

Edit: is it the fix?

Replace

return new Promise(resolve => {
    API.addEventListener(newState => {
        cacheStorage.state = newState;
        resolve(newState); // ??? what if the listener is called again?
    }, /* repeating */ true);
  });

with

return new Promise(resolve => {
    let initialCall = true;
    API.addEventListener(newState => {
        cacheStorage.state = newState;
        if (initialCall) {
          resolve(newState);
          initialCall = false;
        }
    }, /* repeating */ true);
  });
Leedehai
  • 3,660
  • 3
  • 21
  • 44
  • check this link: https://stackoverflow.com/questions/31709987/caching-javascript-promise-results – MajiD Aug 06 '20 at 09:03
  • Hey @MajiD yeah I read it before posting this. That question is not quite the same as my scenario, as that post doesn't worry about API calling the listener on state mutation. In my scenario, the API stores the listener, and may call it multiple times. – Leedehai Aug 06 '20 at 14:39

0 Answers0