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
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
|