cached-proxy
v0.1.2
Published
A cached Proxy with optional timeouts per property to simplify complex remote scenarios
Maintainers
Readme
cached-proxy
Social Media Photo by Bozhin Karaivanov on Unsplash
A cached Proxy with optional timeouts per property to simplify complex remote scenarios.
import CachedProxy from 'https://esm.run/cached-proxy';
const remote = new CachedProxy({ id: 123 }, {
// optional: 0 means ASAP, otherwise cached for N milliseconds
timeout: 0,
// example: compute some remote value synchronously
get(target, property) {
const xhr = new XMLHttpRequest;
xhr.open('GET', 'end/point', false);
xhr.send(JSON.stringify({ trap: 'get', args: [target.id, property] }));
return JSON.parse(xhr.responseText);
}
});
// executes the XHR once, returns whatever value was cached
remote.test === remote.test;
setTimeout(() => {
remote.test; // runs the XHR again
}, 10);The example is there to explain that given enough amount of traps, it is possible via Atomics or synchronous / asynchronous behavior to retrieve once properties and values from elsewhere, similarly to how reflected-ffi memoized cache works but in an easier to orchestrate way.
Traps
| | cached | timeout | drop | reset | | :----------------------- | :----: | :-----: | :--: | :---: | | apply | | | | | | construct | | | | | | defineProperty | | | ☑️ | | | deleteProperty | | | ☑️ | | | get | ☑️ | ☑️ | | | | getOwnPropertyDescriptor | ☑️ | ☑️ | | | | getPrototypeOf | ☑️ | | | | | has | ☑️ | ☑️ | | | | isExtensible | ☑️ | | | | | ownKeys * | ☑️ | ✔️ | | | | preventExtensions | | | | | | set | | | ☑️ | | | setPrototypeOf | | | | ☑️ |
Traps Explainer
- cached means each trap result is weakly stored
- timeout means that, if a
timeoutoptional integer is passed as proxy handler field,get,getOwnPropertyDescriptorandhaswill be dropped after that amount of time (in milliseconds) - drop means that the eventually stored value for that property or accessor will be instantly removed from the cache, affecting also
ownKeysbut without affectingisExtensibleandgetPrototypeOf - reset means that all weakly related values will be erased per property or target reference, effectively invalidating the whole cache for any trap that has one
The drop and reset utilities are also exposed via the module where drop(ref, property) will invalidate both ownKeys and the cache per specific property while reset(ref) will invalidate the whole cache per specific reference.
Please note: the reference is not the proxied one, it's the original one you must own, otherwise nothing will happen/work as expected, example:
import Proxy, { drop, reset } from 'https://esm.run/cached-proxy';
const ref = { my: 'reference' };
const proxied = Proxy(ref);
// when/if needed, this works:
drop(ref, 'property');
// ... or ...
reset(ref);
// while this will not work:
drop(proxied, 'property');
// ... or ...
reset(proxied);This is to avoid leaking the cache intent of the proxy owner/creator.
Special Cases + Cached suffix
- arrays have a
gettrap that resets the cache if the retrieved property is neitherlengthnor anindex(an unsigned integer, 0 to max array length). This makes usage of a timeout almost irrevelant because methods that mutate the array should drop properties as needed. - dom nodes should likely use no cache due their highly mutable nature, however it is possible to use a handler that checks the returned value by specifying a
getCached(target, property, value)that if returnstruewill cache the entry, otherwise it won't cache anything and, if thevalueis a function able to mutate the instance, it should likelyreset(target)reference to avoid any undesired cache.
The Cached suffix can be used for each of these traps:
getCached, to skip caching undesired results or reset the cache in case the value is a function with side effects (i.e. methods that mutate internally the proxied reference)getOwnPropertyDescriptorCached, to skip caching a descriptor or reset the cache in case it is an accessor with side effects (i.e.textContentor others)has, to skip caching a specifickey in proxycheck
