You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
4.9 KiB
113 lines
4.9 KiB
5 months ago
|
import { Trie } from "@wry/trie";
|
||
|
import { StrongCache } from "@wry/caches";
|
||
|
import { Entry } from "./entry.js";
|
||
|
import { parentEntrySlot } from "./context.js";
|
||
|
// These helper functions are important for making optimism work with
|
||
|
// asynchronous code. In order to register parent-child dependencies,
|
||
|
// optimism needs to know about any currently active parent computations.
|
||
|
// In ordinary synchronous code, the parent context is implicit in the
|
||
|
// execution stack, but asynchronous code requires some extra guidance in
|
||
|
// order to propagate context from one async task segment to the next.
|
||
|
export { bindContext, noContext, nonReactive, setTimeout, asyncFromGen, Slot, } from "./context.js";
|
||
|
// A lighter-weight dependency, similar to OptimisticWrapperFunction, except
|
||
|
// with only one argument, no makeCacheKey, no wrapped function to recompute,
|
||
|
// and no result value. Useful for representing dependency leaves in the graph
|
||
|
// of computation. Subscriptions are supported.
|
||
|
export { dep } from "./dep.js";
|
||
|
// The defaultMakeCacheKey function is remarkably powerful, because it gives
|
||
|
// a unique object for any shallow-identical list of arguments. If you need
|
||
|
// to implement a custom makeCacheKey function, you may find it helpful to
|
||
|
// delegate the final work to defaultMakeCacheKey, which is why we export it
|
||
|
// here. However, you may want to avoid defaultMakeCacheKey if your runtime
|
||
|
// does not support WeakMap, or you have the ability to return a string key.
|
||
|
// In those cases, just write your own custom makeCacheKey functions.
|
||
|
let defaultKeyTrie;
|
||
|
export function defaultMakeCacheKey(...args) {
|
||
|
const trie = defaultKeyTrie || (defaultKeyTrie = new Trie(typeof WeakMap === "function"));
|
||
|
return trie.lookupArray(args);
|
||
|
}
|
||
|
// If you're paranoid about memory leaks, or you want to avoid using WeakMap
|
||
|
// under the hood, but you still need the behavior of defaultMakeCacheKey,
|
||
|
// import this constructor to create your own tries.
|
||
|
export { Trie as KeyTrie };
|
||
|
;
|
||
|
const caches = new Set();
|
||
|
export function wrap(originalFunction, { max = Math.pow(2, 16), keyArgs, makeCacheKey = defaultMakeCacheKey, normalizeResult, subscribe, cache: cacheOption = StrongCache, } = Object.create(null)) {
|
||
|
const cache = typeof cacheOption === "function"
|
||
|
? new cacheOption(max, entry => entry.dispose())
|
||
|
: cacheOption;
|
||
|
const optimistic = function () {
|
||
|
const key = makeCacheKey.apply(null, keyArgs ? keyArgs.apply(null, arguments) : arguments);
|
||
|
if (key === void 0) {
|
||
|
return originalFunction.apply(null, arguments);
|
||
|
}
|
||
|
let entry = cache.get(key);
|
||
|
if (!entry) {
|
||
|
cache.set(key, entry = new Entry(originalFunction));
|
||
|
entry.normalizeResult = normalizeResult;
|
||
|
entry.subscribe = subscribe;
|
||
|
// Give the Entry the ability to trigger cache.delete(key), even though
|
||
|
// the Entry itself does not know about key or cache.
|
||
|
entry.forget = () => cache.delete(key);
|
||
|
}
|
||
|
const value = entry.recompute(Array.prototype.slice.call(arguments));
|
||
|
// Move this entry to the front of the least-recently used queue,
|
||
|
// since we just finished computing its value.
|
||
|
cache.set(key, entry);
|
||
|
caches.add(cache);
|
||
|
// Clean up any excess entries in the cache, but only if there is no
|
||
|
// active parent entry, meaning we're not in the middle of a larger
|
||
|
// computation that might be flummoxed by the cleaning.
|
||
|
if (!parentEntrySlot.hasValue()) {
|
||
|
caches.forEach(cache => cache.clean());
|
||
|
caches.clear();
|
||
|
}
|
||
|
return value;
|
||
|
};
|
||
|
Object.defineProperty(optimistic, "size", {
|
||
|
get: () => cache.size,
|
||
|
configurable: false,
|
||
|
enumerable: false,
|
||
|
});
|
||
|
Object.freeze(optimistic.options = {
|
||
|
max,
|
||
|
keyArgs,
|
||
|
makeCacheKey,
|
||
|
normalizeResult,
|
||
|
subscribe,
|
||
|
cache,
|
||
|
});
|
||
|
function dirtyKey(key) {
|
||
|
const entry = key && cache.get(key);
|
||
|
if (entry) {
|
||
|
entry.setDirty();
|
||
|
}
|
||
|
}
|
||
|
optimistic.dirtyKey = dirtyKey;
|
||
|
optimistic.dirty = function dirty() {
|
||
|
dirtyKey(makeCacheKey.apply(null, arguments));
|
||
|
};
|
||
|
function peekKey(key) {
|
||
|
const entry = key && cache.get(key);
|
||
|
if (entry) {
|
||
|
return entry.peek();
|
||
|
}
|
||
|
}
|
||
|
optimistic.peekKey = peekKey;
|
||
|
optimistic.peek = function peek() {
|
||
|
return peekKey(makeCacheKey.apply(null, arguments));
|
||
|
};
|
||
|
function forgetKey(key) {
|
||
|
return key ? cache.delete(key) : false;
|
||
|
}
|
||
|
optimistic.forgetKey = forgetKey;
|
||
|
optimistic.forget = function forget() {
|
||
|
return forgetKey(makeCacheKey.apply(null, arguments));
|
||
|
};
|
||
|
optimistic.makeCacheKey = makeCacheKey;
|
||
|
optimistic.getKey = keyArgs ? function getKey() {
|
||
|
return makeCacheKey.apply(null, keyArgs.apply(null, arguments));
|
||
|
} : makeCacheKey;
|
||
|
return Object.freeze(optimistic);
|
||
|
}
|
||
|
//# sourceMappingURL=index.js.map
|