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), ifrepeating
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);
});