Comparing version
@@ -95,3 +95,2 @@ 'use strict'; | ||
var reusableEmptyArray = []; | ||
var emptySetPool = []; | ||
@@ -139,2 +138,3 @@ var POOL_TARGET_SIZE = 100; | ||
this.value = []; | ||
this.sets = null; | ||
++Entry.count; | ||
@@ -150,8 +150,3 @@ } | ||
assert(!this.recomputing, "already recomputing"); | ||
if (!rememberParent(this) && maybeReportOrphan(this)) { | ||
// The recipient of the entry.reportOrphan callback decided to dispose | ||
// of this orphan entry by calling entry.dispose(), so we don't need to | ||
// (and should not) proceed with the recomputation. | ||
return void 0; | ||
} | ||
rememberParent(this); | ||
return mightBeDirty(this) | ||
@@ -167,2 +162,3 @@ ? reallyRecompute(this) | ||
reportDirty(this); | ||
forgetChildren(this); | ||
// We can go ahead and unsubscribe here, since any further dirty | ||
@@ -175,3 +171,3 @@ // notifications we receive will be redundant, and unsubscribing may | ||
var _this = this; | ||
forgetChildren(this).forEach(maybeReportOrphan); | ||
forgetChildren(this); | ||
maybeUnsubscribe(this); | ||
@@ -194,2 +190,18 @@ // Because this entry has been kicked out of the cache (in index.js), | ||
}; | ||
Entry.prototype.addToSet = function (entrySet) { | ||
entrySet.add(this); | ||
if (!this.sets) { | ||
this.sets = emptySetPool.pop() || new Set(); | ||
} | ||
this.sets.add(entrySet); | ||
}; | ||
Entry.prototype.removeFromSets = function () { | ||
var _this = this; | ||
if (this.sets) { | ||
this.sets.forEach(function (set) { return set.delete(_this); }); | ||
this.sets.clear(); | ||
emptySetPool.push(this.sets); | ||
this.sets = null; | ||
} | ||
}; | ||
Entry.count = 0; | ||
@@ -215,6 +227,3 @@ return Entry; | ||
function reallyRecompute(entry) { | ||
// Since this recomputation is likely to re-remember some of this | ||
// entry's children, we forget our children here but do not call | ||
// maybeReportOrphan until after the recomputation finishes. | ||
var originalChildren = forgetChildren(entry); | ||
forgetChildren(entry); | ||
// Set entry as the parent entry while calling recomputeNewValue(entry). | ||
@@ -227,6 +236,2 @@ parentEntrySlot.withValue(entry, recomputeNewValue, [entry]); | ||
} | ||
// Now that we've had a chance to re-remember any children that were | ||
// involved in the recomputation, we can safely report any orphan | ||
// children that remain. | ||
originalChildren.forEach(maybeReportOrphan); | ||
return valueGet(entry.value); | ||
@@ -316,27 +321,16 @@ } | ||
} | ||
// If the given entry has a reportOrphan method, and no remaining parents, | ||
// call entry.reportOrphan and return true iff it returns true. The | ||
// reportOrphan function should return true to indicate entry.dispose() | ||
// has been called, and the entry has been removed from any other caches | ||
// (see index.js for the only current example). | ||
function maybeReportOrphan(entry) { | ||
return entry.parents.size === 0 && | ||
typeof entry.reportOrphan === "function" && | ||
entry.reportOrphan() === true; | ||
} | ||
// Removes all children from this entry and returns an array of the | ||
// removed children. | ||
function forgetChildren(parent) { | ||
var children = reusableEmptyArray; | ||
if (parent.childValues.size > 0) { | ||
children = []; | ||
parent.childValues.forEach(function (_value, child) { | ||
forgetChild(parent, child); | ||
children.push(child); | ||
}); | ||
} | ||
// Remove this parent Entry from any sets to which it was added by the | ||
// addToSet method. | ||
parent.removeFromSets(); | ||
// After we forget all our children, this.dirtyChildren must be empty | ||
// and therefore must have been reset to null. | ||
assert(parent.dirtyChildren === null); | ||
return children; | ||
} | ||
@@ -435,13 +429,4 @@ function forgetChild(parent, child) { | ||
var cache = new Cache(options.max || Math.pow(2, 16), function (entry) { return entry.dispose(); }); | ||
var disposable = !!options.disposable; | ||
var makeCacheKey = options.makeCacheKey || defaultMakeCacheKey; | ||
function optimistic() { | ||
if (disposable && !parentEntrySlot.hasValue()) { | ||
// If there's no current parent computation, and this wrapped | ||
// function is disposable (meaning we don't care about entry.value, | ||
// just dependency tracking), then we can short-cut everything else | ||
// in this function, because entry.recompute() is going to recycle | ||
// the entry object without recomputing anything, anyway. | ||
return void 0; | ||
} | ||
var key = makeCacheKey.apply(null, arguments); | ||
@@ -460,5 +445,2 @@ if (key === void 0) { | ||
entry.subscribe = options.subscribe; | ||
if (disposable) { | ||
entry.reportOrphan = function () { return cache.delete(key); }; | ||
} | ||
} | ||
@@ -477,6 +459,3 @@ var value = entry.recompute(); | ||
} | ||
// If options.disposable is truthy, the caller of wrap is telling us | ||
// they don't care about the result of entry.recompute(), so we should | ||
// avoid returning the value, so it won't be accidentally used. | ||
return disposable ? void 0 : value; | ||
return value; | ||
} | ||
@@ -492,2 +471,23 @@ optimistic.dirty = function () { | ||
} | ||
function dep() { | ||
var parentEntriesByKey = new Map(); | ||
function depend(key) { | ||
var parent = parentEntrySlot.getValue(); | ||
if (parent) { | ||
var parentEntrySet = parentEntriesByKey.get(key); | ||
if (!parentEntrySet) { | ||
parentEntriesByKey.set(key, parentEntrySet = new Set); | ||
} | ||
parent.addToSet(parentEntrySet); | ||
} | ||
} | ||
depend.dirty = function (key) { | ||
var parentEntrySet = parentEntriesByKey.get(key); | ||
if (parentEntrySet) { | ||
parentEntrySet.forEach(function (entry) { return entry.setDirty(); }); | ||
parentEntriesByKey.delete(key); | ||
} | ||
}; | ||
return depend; | ||
} | ||
@@ -520,3 +520,4 @@ Object.defineProperty(exports, 'asyncFromGen', { | ||
exports.defaultMakeCacheKey = defaultMakeCacheKey; | ||
exports.dep = dep; | ||
exports.wrap = wrap; | ||
//# sourceMappingURL=bundle.cjs.js.map |
@@ -92,3 +92,2 @@ import { Slot } from '@wry/context'; | ||
var reusableEmptyArray = []; | ||
var emptySetPool = []; | ||
@@ -136,2 +135,3 @@ var POOL_TARGET_SIZE = 100; | ||
this.value = []; | ||
this.sets = null; | ||
++Entry.count; | ||
@@ -147,8 +147,3 @@ } | ||
assert(!this.recomputing, "already recomputing"); | ||
if (!rememberParent(this) && maybeReportOrphan(this)) { | ||
// The recipient of the entry.reportOrphan callback decided to dispose | ||
// of this orphan entry by calling entry.dispose(), so we don't need to | ||
// (and should not) proceed with the recomputation. | ||
return void 0; | ||
} | ||
rememberParent(this); | ||
return mightBeDirty(this) | ||
@@ -164,2 +159,3 @@ ? reallyRecompute(this) | ||
reportDirty(this); | ||
forgetChildren(this); | ||
// We can go ahead and unsubscribe here, since any further dirty | ||
@@ -172,3 +168,3 @@ // notifications we receive will be redundant, and unsubscribing may | ||
var _this = this; | ||
forgetChildren(this).forEach(maybeReportOrphan); | ||
forgetChildren(this); | ||
maybeUnsubscribe(this); | ||
@@ -191,2 +187,18 @@ // Because this entry has been kicked out of the cache (in index.js), | ||
}; | ||
Entry.prototype.addToSet = function (entrySet) { | ||
entrySet.add(this); | ||
if (!this.sets) { | ||
this.sets = emptySetPool.pop() || new Set(); | ||
} | ||
this.sets.add(entrySet); | ||
}; | ||
Entry.prototype.removeFromSets = function () { | ||
var _this = this; | ||
if (this.sets) { | ||
this.sets.forEach(function (set) { return set.delete(_this); }); | ||
this.sets.clear(); | ||
emptySetPool.push(this.sets); | ||
this.sets = null; | ||
} | ||
}; | ||
Entry.count = 0; | ||
@@ -212,6 +224,3 @@ return Entry; | ||
function reallyRecompute(entry) { | ||
// Since this recomputation is likely to re-remember some of this | ||
// entry's children, we forget our children here but do not call | ||
// maybeReportOrphan until after the recomputation finishes. | ||
var originalChildren = forgetChildren(entry); | ||
forgetChildren(entry); | ||
// Set entry as the parent entry while calling recomputeNewValue(entry). | ||
@@ -224,6 +233,2 @@ parentEntrySlot.withValue(entry, recomputeNewValue, [entry]); | ||
} | ||
// Now that we've had a chance to re-remember any children that were | ||
// involved in the recomputation, we can safely report any orphan | ||
// children that remain. | ||
originalChildren.forEach(maybeReportOrphan); | ||
return valueGet(entry.value); | ||
@@ -313,27 +318,16 @@ } | ||
} | ||
// If the given entry has a reportOrphan method, and no remaining parents, | ||
// call entry.reportOrphan and return true iff it returns true. The | ||
// reportOrphan function should return true to indicate entry.dispose() | ||
// has been called, and the entry has been removed from any other caches | ||
// (see index.js for the only current example). | ||
function maybeReportOrphan(entry) { | ||
return entry.parents.size === 0 && | ||
typeof entry.reportOrphan === "function" && | ||
entry.reportOrphan() === true; | ||
} | ||
// Removes all children from this entry and returns an array of the | ||
// removed children. | ||
function forgetChildren(parent) { | ||
var children = reusableEmptyArray; | ||
if (parent.childValues.size > 0) { | ||
children = []; | ||
parent.childValues.forEach(function (_value, child) { | ||
forgetChild(parent, child); | ||
children.push(child); | ||
}); | ||
} | ||
// Remove this parent Entry from any sets to which it was added by the | ||
// addToSet method. | ||
parent.removeFromSets(); | ||
// After we forget all our children, this.dirtyChildren must be empty | ||
// and therefore must have been reset to null. | ||
assert(parent.dirtyChildren === null); | ||
return children; | ||
} | ||
@@ -432,13 +426,4 @@ function forgetChild(parent, child) { | ||
var cache = new Cache(options.max || Math.pow(2, 16), function (entry) { return entry.dispose(); }); | ||
var disposable = !!options.disposable; | ||
var makeCacheKey = options.makeCacheKey || defaultMakeCacheKey; | ||
function optimistic() { | ||
if (disposable && !parentEntrySlot.hasValue()) { | ||
// If there's no current parent computation, and this wrapped | ||
// function is disposable (meaning we don't care about entry.value, | ||
// just dependency tracking), then we can short-cut everything else | ||
// in this function, because entry.recompute() is going to recycle | ||
// the entry object without recomputing anything, anyway. | ||
return void 0; | ||
} | ||
var key = makeCacheKey.apply(null, arguments); | ||
@@ -457,5 +442,2 @@ if (key === void 0) { | ||
entry.subscribe = options.subscribe; | ||
if (disposable) { | ||
entry.reportOrphan = function () { return cache.delete(key); }; | ||
} | ||
} | ||
@@ -474,6 +456,3 @@ var value = entry.recompute(); | ||
} | ||
// If options.disposable is truthy, the caller of wrap is telling us | ||
// they don't care about the result of entry.recompute(), so we should | ||
// avoid returning the value, so it won't be accidentally used. | ||
return disposable ? void 0 : value; | ||
return value; | ||
} | ||
@@ -489,4 +468,25 @@ optimistic.dirty = function () { | ||
} | ||
function dep() { | ||
var parentEntriesByKey = new Map(); | ||
function depend(key) { | ||
var parent = parentEntrySlot.getValue(); | ||
if (parent) { | ||
var parentEntrySet = parentEntriesByKey.get(key); | ||
if (!parentEntrySet) { | ||
parentEntriesByKey.set(key, parentEntrySet = new Set); | ||
} | ||
parent.addToSet(parentEntrySet); | ||
} | ||
} | ||
depend.dirty = function (key) { | ||
var parentEntrySet = parentEntriesByKey.get(key); | ||
if (parentEntrySet) { | ||
parentEntrySet.forEach(function (entry) { return entry.setDirty(); }); | ||
parentEntriesByKey.delete(key); | ||
} | ||
}; | ||
return depend; | ||
} | ||
export { KeyTrie, defaultMakeCacheKey, wrap }; | ||
export { KeyTrie, defaultMakeCacheKey, dep, wrap }; | ||
//# sourceMappingURL=bundle.esm.js.map |
@@ -10,3 +10,2 @@ import { OptimisticWrapOptions } from "./index"; | ||
unsubscribe?: () => any; | ||
reportOrphan?: (this: Entry<TArgs, TValue>) => any; | ||
readonly parents: Set<Entry<any, any>>; | ||
@@ -22,3 +21,6 @@ readonly childValues: Map<Entry<any, any>, Value<any>>; | ||
dispose(): void; | ||
private sets; | ||
addToSet(entrySet: Set<AnyEntry>): void; | ||
removeFromSets(): void; | ||
} | ||
export {}; |
@@ -9,5 +9,7 @@ import { KeyTrie } from "./key-trie"; | ||
}; | ||
declare type OptimisticDependencyFunction<TKey> = ((key: TKey) => void) & { | ||
dirty: (key: TKey) => void; | ||
}; | ||
export declare type OptimisticWrapOptions<TArgs extends any[]> = { | ||
max?: number; | ||
disposable?: boolean; | ||
makeCacheKey?: (...args: TArgs) => TCacheKey; | ||
@@ -17,1 +19,2 @@ subscribe?: (...args: TArgs) => (() => any) | undefined; | ||
export declare function wrap<TArgs extends any[], TResult>(originalFunction: (...args: TArgs) => TResult, options?: OptimisticWrapOptions<TArgs>): OptimisticWrapperFunction<TArgs, TResult>; | ||
export declare function dep<TKey>(): OptimisticDependencyFunction<TKey>; |
{ | ||
"name": "optimism", | ||
"version": "0.10.3", | ||
"version": "0.11.0", | ||
"author": "Ben Newman <ben@benjamn.com>", | ||
@@ -5,0 +5,0 @@ "description": "Composable reactive caching with efficient invalidation.", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1054
0.57%103946
-2.72%