Skip to content

Commit 159ae44

Browse files
committedMay 28, 2021
fix(cache): clear entries references with greater precision
1 parent 4160352 commit 159ae44

File tree

3 files changed

+47
-71
lines changed

3 files changed

+47
-71
lines changed
 

‎src/cache.js

+23-40
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as emitter from "./emitter.js";
22

33
const entries = new WeakMap();
44
const suspense = new WeakSet();
5+
const values = new WeakMap();
56

67
export function getEntry(target, key) {
78
let targetMap = entries.get(target);
@@ -24,6 +25,14 @@ export function getEntry(target, key) {
2425
resolved: false,
2526
};
2627
targetMap.set(key, entry);
28+
} else if (entry.contexts.size) {
29+
entry.contexts.forEach(contextEntry => {
30+
if (suspense.has(contextEntry.target)) {
31+
entry.contexts.delete(contextEntry);
32+
contextEntry.depState = 0;
33+
contextEntry.resolved = false;
34+
}
35+
});
2736
}
2837

2938
return entry;
@@ -43,7 +52,10 @@ export function getEntries(target) {
4352
function dispatchDeep(entry) {
4453
entry.resolved = false;
4554

46-
emitter.dispatch(entry);
55+
if (!suspense.has(entry.target)) {
56+
emitter.dispatch(entry);
57+
}
58+
4759
entry.contexts.forEach(dispatchDeep);
4860
}
4961

@@ -52,10 +64,6 @@ const contexts = new Set();
5264
export function get(target, key, getter, validate) {
5365
const entry = getEntry(target, key);
5466

55-
if (contexts.has(entry)) {
56-
throw Error(`Circular get invocation is forbidden: '${key}'`);
57-
}
58-
5967
if (context && !suspense.has(context.target)) {
6068
context.deps.add(entry);
6169
entry.contexts.add(context);
@@ -88,6 +96,10 @@ export function get(target, key, getter, validate) {
8896
}
8997
}
9098

99+
if (contexts.has(entry)) {
100+
throw Error(`Circular get invocation is forbidden: '${key}'`);
101+
}
102+
91103
const lastContext = context;
92104

93105
try {
@@ -143,6 +155,7 @@ export function set(target, key, setter, value) {
143155
if (newValue !== entry.value) {
144156
entry.value = newValue;
145157
entry.state += 1;
158+
entry.depState = 0;
146159

147160
dispatchDeep(entry);
148161
}
@@ -209,54 +222,24 @@ export function invalidateAll(target, clearValue, deleteValue) {
209222
}
210223
}
211224

212-
const values = new WeakMap();
213225
export function observe(target, key, getter, fn) {
214226
const entry = getEntry(target, key);
215227

216228
return emitter.subscribe(entry, () => {
217-
if (!suspense.has(target)) {
218-
const lastValue = values.get(entry);
219-
const value = get(target, key, getter);
229+
const lastValue = values.get(entry);
230+
const value = get(target, key, getter);
220231

221-
if (value !== lastValue) {
222-
fn(target, value, lastValue);
223-
values.set(entry, value);
224-
}
232+
if (value !== lastValue) {
233+
fn(target, value, lastValue);
234+
values.set(entry, value);
225235
}
226236
});
227237
}
228238

229-
const clearTargets = new Set();
230-
export function clear(target) {
231-
if (clearTargets.size === 0) {
232-
requestAnimationFrame(() => {
233-
clearTargets.forEach(t => {
234-
const targetMap = entries.get(t);
235-
if (targetMap) {
236-
targetMap.forEach(entry => {
237-
entry.resolved = false;
238-
239-
entry.deps.forEach(depEntry => {
240-
depEntry.contexts.delete(entry);
241-
});
242-
243-
entry.deps.clear();
244-
entry.contexts.clear();
245-
});
246-
}
247-
});
248-
249-
clearTargets.clear();
250-
});
251-
}
252-
clearTargets.add(target);
253-
}
254-
255239
export function suspend(target) {
256240
suspense.add(target);
257241
}
258242

259243
export function unsuspend(target) {
260244
suspense.delete(target);
261-
clearTargets.delete(target);
262245
}

‎src/define.js

-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ export function defineElement(tagName, hybrids) {
191191
}
192192

193193
cache.suspend(this);
194-
cache.clear(this);
195194
}
196195
}
197196

‎test/spec/cache.js

+24-30
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
suspend,
99
unsuspend,
1010
getEntry,
11-
clear,
1211
} from "../../src/cache.js";
1312

1413
describe("cache:", () => {
@@ -339,34 +338,6 @@ describe("cache:", () => {
339338
});
340339
});
341340

342-
describe("clear()", () => {
343-
it("removes references from deps", done => {
344-
const dep = {};
345-
get(target, "key", () => get(dep, "value", () => "value"));
346-
const hasTarget = () =>
347-
[...getEntry(dep, "value").contexts].some(
348-
entry => entry.target === target,
349-
);
350-
expect(hasTarget()).toBe(true);
351-
352-
clear(target);
353-
354-
requestAnimationFrame(() => {
355-
expect(hasTarget()).toBe(false);
356-
done();
357-
});
358-
});
359-
360-
it("skip when already added", done => {
361-
clear(target);
362-
clear(target);
363-
364-
requestAnimationFrame(() => {
365-
done();
366-
});
367-
});
368-
});
369-
370341
describe("suspend()", () => {
371342
it("disables cache hits", () => {
372343
get(target, "key", spy);
@@ -395,7 +366,30 @@ describe("cache:", () => {
395366
observe(target, "key", (_, v) => v, spy);
396367

397368
requestAnimationFrame(() => {
398-
expect(spy).not.toHaveBeenCalled();
369+
expect(spy).toHaveBeenCalledTimes(1);
370+
371+
set(target, "key", () => "new value");
372+
requestAnimationFrame(() => {
373+
expect(spy).toHaveBeenCalledTimes(1);
374+
done();
375+
});
376+
});
377+
});
378+
379+
it("removes references from deps when dep is called", done => {
380+
const dep = {};
381+
get(target, "key", () => get(dep, "value", () => "value"));
382+
const hasTarget = () =>
383+
[...getEntry(dep, "value").contexts].some(
384+
entry => entry.target === target,
385+
);
386+
expect(hasTarget()).toBe(true);
387+
388+
suspend(target);
389+
get(dep, "value", () => "value");
390+
391+
requestAnimationFrame(() => {
392+
expect(hasTarget()).toBe(false);
399393
done();
400394
});
401395
});

0 commit comments

Comments
 (0)
Please sign in to comment.