Skip to content

Commit be1d622

Browse files
committed
Merge pull request #11819 from emberjs/abstract-chainwatchers
[CLEANUP beta] Abstract chainWatchers into an object.
2 parents 665c733 + 2f63cab commit be1d622

File tree

4 files changed

+143
-137
lines changed

4 files changed

+143
-137
lines changed

packages/ember-metal/lib/chains.js

+127-83
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,93 @@ function Chains() { }
2121

2222
Chains.prototype = Object.create(null);
2323

24+
function ChainWatchers(obj) {
25+
// this obj would be the referencing chain node's parent node's value
26+
this.obj = obj;
27+
// chain nodes that reference a key in this obj by key
28+
// we only create ChainWatchers when we are going to add them
29+
// so create this upfront
30+
this.chains = new Chains();
31+
}
32+
33+
ChainWatchers.prototype = {
34+
add(key, node) {
35+
let nodes = this.chains[key];
36+
if (nodes === undefined) {
37+
this.chains[key] = [node];
38+
} else {
39+
nodes.push(node);
40+
}
41+
},
42+
43+
remove(key, node) {
44+
let nodes = this.chains[key];
45+
if (nodes) {
46+
for (var i = 0, l = nodes.length; i < l; i++) {
47+
if (nodes[i] === node) {
48+
nodes.splice(i, 1);
49+
break;
50+
}
51+
}
52+
}
53+
},
54+
55+
has(key, node) {
56+
let nodes = this.chains[key];
57+
if (nodes) {
58+
for (var i = 0, l = nodes.length; i < l; i++) {
59+
if (nodes[i] === node) {
60+
return true;
61+
}
62+
}
63+
}
64+
return false;
65+
},
66+
67+
revalidateAll() {
68+
for (let key in this.chains) {
69+
this.notify(key, true, undefined);
70+
}
71+
},
72+
73+
revalidate(key) {
74+
this.notify(key, true, undefined);
75+
},
76+
77+
// key: the string key that is part of a path changed
78+
// revalidate: boolean the chains that are watching this value should revalidate
79+
// callback: function that will be called with the the object and path that
80+
// will be/are invalidated by this key change depending on the
81+
// whether the revalidate flag is passed
82+
notify(key, revalidate, callback) {
83+
let nodes = this.chains[key];
84+
if (nodes === undefined || nodes.length === 0) {
85+
return;
86+
}
87+
88+
let affected;
89+
90+
if (callback) {
91+
affected = [];
92+
}
93+
94+
for (let i = 0, l = nodes.length; i < l; i++) {
95+
nodes[i].notify(revalidate, affected);
96+
}
97+
98+
if (callback === undefined) {
99+
return;
100+
}
101+
102+
// we gather callbacks so we don't notify them during revalidation
103+
for (let i = 0, l = affected.length; i < l; i += 2) {
104+
let obj = affected[i];
105+
let path = affected[i + 1];
106+
callback(obj, path);
107+
}
108+
}
109+
};
110+
24111
var pendingQueue = [];
25112

26113
// attempts to add the pendingQueue chains again. If some of them end up
@@ -49,19 +136,13 @@ function addChainWatcher(obj, keyName, node) {
49136
return;
50137
}
51138

52-
var m = metaFor(obj);
53-
var nodes = m.chainWatchers;
139+
let m = metaFor(obj);
54140

55-
// TODO remove hasOwnProperty check
56-
if (nodes === undefined || !m.hasOwnProperty('chainWatchers')) {
57-
nodes = m.chainWatchers = new Chains();
141+
if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) {
142+
m.chainWatchers = new ChainWatchers(obj);
58143
}
59144

60-
if (!nodes[keyName]) {
61-
nodes[keyName] = [node];
62-
} else {
63-
nodes[keyName].push(node);
64-
}
145+
m.chainWatchers.add(keyName, node);
65146

66147
watchKey(obj, keyName, m);
67148
}
@@ -71,20 +152,18 @@ function removeChainWatcher(obj, keyName, node) {
71152
return;
72153
}
73154

74-
var m = obj['__ember_meta__'];
75-
if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do
76-
77-
var nodes = m && m.chainWatchers;
155+
let m = obj.__ember_meta__;
78156

79-
if (nodes && nodes[keyName]) {
80-
nodes = nodes[keyName];
81-
for (var i = 0, l = nodes.length; i < l; i++) {
82-
if (nodes[i] === node) {
83-
nodes.splice(i, 1);
84-
break;
85-
}
86-
}
157+
if (!m ||
158+
m.chainWatchers === undefined || m.chainWatchers.obj !== obj) {
159+
return;
87160
}
161+
162+
// make meta writable
163+
m = metaFor(obj);
164+
165+
m.chainWatchers.remove(keyName, node);
166+
88167
unwatchKey(obj, keyName, m);
89168
}
90169

@@ -287,44 +366,8 @@ ChainNode.prototype = {
287366
}
288367
},
289368

290-
willChange(events) {
291-
var chains = this._chains;
292-
var node;
293-
if (chains) {
294-
for (var key in chains) {
295-
node = chains[key];
296-
if (node !== undefined) {
297-
node.willChange(events);
298-
}
299-
}
300-
}
301-
302-
if (this._parent) {
303-
this._parent.notifyChainChange(this, this._key, 1, events);
304-
}
305-
},
306-
307-
notifyChainChange(chain, path, depth, events) {
308-
if (this._key) {
309-
path = this._key + '.' + path;
310-
}
311-
312-
if (this._parent) {
313-
this._parent.notifyChainChange(this, path, depth + 1, events);
314-
} else {
315-
if (depth > 1) {
316-
events.push(this.value(), path);
317-
}
318-
path = 'this.' + path;
319-
if (this._paths[path] > 0) {
320-
events.push(this.value(), path);
321-
}
322-
}
323-
},
324-
325-
didChange(events) {
326-
// invalidate my own value first.
327-
if (this._watching) {
369+
notify(revalidate, affected) {
370+
if (revalidate && this._watching) {
328371
var obj = this._parent.value();
329372
if (obj !== this._object) {
330373
removeChainWatcher(this._object, this._key, this);
@@ -347,48 +390,49 @@ ChainNode.prototype = {
347390
for (var key in chains) {
348391
node = chains[key];
349392
if (node !== undefined) {
350-
node.didChange(events);
393+
node.notify(revalidate, affected);
351394
}
352395
}
353396
}
354397

355-
// if no events are passed in then we only care about the above wiring update
356-
if (events === null) {
357-
return;
398+
if (affected && this._parent) {
399+
this._parent.populateAffected(this, this._key, 1, affected);
400+
}
401+
},
402+
403+
populateAffected(chain, path, depth, affected) {
404+
if (this._key) {
405+
path = this._key + '.' + path;
358406
}
359407

360-
// and finally tell parent about my path changing...
361408
if (this._parent) {
362-
this._parent.notifyChainChange(this, this._key, 1, events);
409+
this._parent.populateAffected(this, path, depth + 1, affected);
410+
} else {
411+
if (depth > 1) {
412+
affected.push(this.value(), path);
413+
}
414+
path = 'this.' + path;
415+
if (this._paths[path] > 0) {
416+
affected.push(this.value(), path);
417+
}
363418
}
364419
}
365420
};
366421

367422
export function finishChains(obj) {
368423
// We only create meta if we really have to
369-
var m = obj['__ember_meta__'];
370-
var chains, chainWatchers, chainNodes;
371-
424+
let m = obj.__ember_meta__;
372425
if (m) {
373426
// finish any current chains node watchers that reference obj
374-
chainWatchers = m.chainWatchers;
427+
let chainWatchers = m.chainWatchers;
375428
if (chainWatchers) {
376-
for (var key in chainWatchers) {
377-
chainNodes = chainWatchers[key];
378-
if (chainNodes) {
379-
for (var i = 0, l = chainNodes.length; i < l; i++) {
380-
var node = chainNodes[i];
381-
if (node) {
382-
node.didChange(null);
383-
}
384-
}
385-
}
386-
}
429+
chainWatchers.revalidateAll();
387430
}
388431
// copy chains from prototype
389-
chains = m.chains;
432+
let chains = m.chains;
390433
if (chains && chains.value() !== obj) {
391-
metaFor(obj).chains = chains = chains.copy(obj);
434+
// need to check if meta is writable
435+
metaFor(obj).chains = chains.copy(obj);
392436
}
393437
}
394438
}

packages/ember-metal/lib/computed.js

+3-10
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,6 @@ ComputedPropertyPrototype.didChange = function(obj, keyName) {
274274
}
275275
};
276276

277-
function finishChains(chainNodes) {
278-
for (var i = 0, l = chainNodes.length; i < l; i++) {
279-
chainNodes[i].didChange(null);
280-
}
281-
}
282-
283277
/**
284278
Access the value of the function backing the computed property.
285279
If this property has already been cached, return the cached result.
@@ -308,7 +302,7 @@ function finishChains(chainNodes) {
308302
@public
309303
*/
310304
ComputedPropertyPrototype.get = function(obj, keyName) {
311-
var ret, cache, meta, chainNodes;
305+
var ret, cache, meta;
312306
if (this._cacheable) {
313307
meta = metaFor(obj);
314308
cache = meta.cache;
@@ -332,9 +326,8 @@ ComputedPropertyPrototype.get = function(obj, keyName) {
332326
cache[keyName] = ret;
333327
}
334328

335-
chainNodes = meta.chainWatchers && meta.chainWatchers[keyName];
336-
if (chainNodes) {
337-
finishChains(chainNodes);
329+
if (meta.chainWatchers) {
330+
meta.chainWatchers.revalidate(keyName);
338331
}
339332
addDependentKeys(this, obj, keyName, meta);
340333
} else {

packages/ember-metal/lib/property_events.js

+9-36
Original file line numberDiff line numberDiff line change
@@ -191,53 +191,26 @@ function iterDeps(method, obj, deps, depKey, seen, meta) {
191191
}
192192

193193
function chainsWillChange(obj, keyName, m) {
194-
if (m.chainWatchers === undefined ||
195-
!m.hasOwnProperty('chainWatchers')) {
194+
if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) {
196195
return;
197196
}
198-
var nodes = m.chainWatchers[keyName];
199-
if (nodes === undefined) {
200-
return;
201-
}
202-
var events = [];
203-
var i, l;
204-
205-
for (i = 0, l = nodes.length; i < l; i++) {
206-
nodes[i].willChange(events);
207-
}
208197

209-
for (i = 0, l = events.length; i < l; i += 2) {
210-
propertyWillChange(events[i], events[i + 1]);
211-
}
198+
m.chainWatchers.notify(keyName, false, propertyWillChange);
212199
}
213200

214-
function chainsDidChange(obj, keyName, m, suppressEvents) {
215-
if (m.chainWatchers === undefined ||
216-
!m.hasOwnProperty('chainWatchers')) {
217-
return;
218-
}
219-
var nodes = m.chainWatchers[keyName];
220-
if (nodes === undefined) {
201+
function chainsDidChange(obj, keyName, m) {
202+
if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) {
221203
return;
222204
}
223-
var events = suppressEvents ? null : [];
224-
var i, l;
225205

226-
for (i = 0, l = nodes.length; i < l; i++) {
227-
nodes[i].didChange(events);
228-
}
229-
230-
if (suppressEvents) {
231-
return;
232-
}
233-
234-
for (i = 0, l = events.length; i < l; i += 2) {
235-
propertyDidChange(events[i], events[i + 1]);
236-
}
206+
m.chainWatchers.notify(keyName, true, propertyDidChange);
237207
}
238208

239209
function overrideChains(obj, keyName, m) {
240-
chainsDidChange(obj, keyName, m, true);
210+
if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) {
211+
return;
212+
}
213+
m.chainWatchers.revalidate(keyName);
241214
}
242215

243216
/**

0 commit comments

Comments
 (0)