Skip to content

Commit 7b723b2

Browse files
committed
Simplify ObserverSet implementation
1 parent e8ff509 commit 7b723b2

File tree

7 files changed

+41
-89
lines changed

7 files changed

+41
-89
lines changed

.eslintrc-browser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ module.exports = {
2929
'clearInterval': true,
3030
'console': true,
3131

32+
'Map': true,
3233
'Set': true,
3334
'Symbol': true,
3435
'WeakMap': true,

packages/ember-metal/lib/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ export { default as isEmpty } from './is_empty';
5252
export { default as isBlank } from './is_blank';
5353
export { default as isPresent } from './is_present';
5454
export { default as run } from './run_loop';
55-
export { default as ObserverSet } from './observer_set';
5655
export {
5756
beginPropertyChanges,
5857
changeProperties,
Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,55 @@
1-
import { guidFor } from 'ember-utils';
21
import { sendEvent } from './events';
32

4-
/*
5-
this.observerSet = {
6-
[senderGuid]: { // variable name: `keySet`
7-
[keyName]: listIndex
8-
}
9-
},
10-
this.observers = [
11-
{
12-
sender: obj,
13-
keyName: keyName,
14-
eventName: eventName,
15-
listeners: [
16-
[target, method, flags]
17-
]
18-
},
19-
...
20-
]
3+
/**
4+
ObserverSet is a data structure used to keep track of observers
5+
that have been deferred.
6+
7+
It ensures that observers are called in the same order that they
8+
were initially triggered.
9+
10+
It also ensures that observers for any object-key pairs are called
11+
only once, even if they were triggered multiple times while
12+
deferred. In this case, the order that the observer is called in
13+
will depend on the first time the observer was triggered.
14+
15+
@private
16+
@class ObserverSet
2117
*/
2218
export default class ObserverSet {
2319
constructor() {
24-
this.clear();
20+
this.added = new Map();
21+
this.queue = [];
2522
}
2623

27-
add(sender, keyName, eventName) {
28-
let observerSet = this.observerSet;
29-
let observers = this.observers;
30-
let senderGuid = guidFor(sender);
31-
let keySet = observerSet[senderGuid];
32-
33-
if (keySet === undefined) {
34-
observerSet[senderGuid] = keySet = {};
24+
add(object, key) {
25+
let keys = this.added.get(object);
26+
if (keys === undefined) {
27+
keys = new Set();
28+
this.added.set(object, keys);
3529
}
3630

37-
let index = keySet[keyName];
38-
if (index === undefined) {
39-
index = observers.push({
40-
sender,
41-
keyName,
42-
eventName,
43-
listeners: []
44-
}) - 1;
45-
keySet[keyName] = index;
31+
if (!keys.has(key)) {
32+
this.queue.push({ object, key });
33+
keys.add(key);
4634
}
47-
return observers[index].listeners;
4835
}
4936

5037
flush() {
51-
let observers = this.observers;
52-
let observer, sender;
53-
this.clear();
54-
for (let i = 0; i < observers.length; ++i) {
55-
observer = observers[i];
56-
sender = observer.sender;
57-
if (sender.isDestroying || sender.isDestroyed) { continue; }
58-
sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners);
38+
for (let i = 0; i < this.queue.length; i++) {
39+
let { object, key } = this.queue[i];
40+
41+
if (object.isDestroying || object.isDestroyed) {
42+
continue;
43+
}
44+
45+
sendEvent(object, key, [object, key]);
5946
}
47+
48+
this.clear();
6049
}
6150

6251
clear() {
63-
this.observerSet = {};
64-
this.observers = [];
52+
this.added.clear();
53+
this.queue.length = 0;
6554
}
6655
}

packages/ember-metal/lib/property_events.js

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -220,46 +220,11 @@ function changeProperties(callback) {
220220
}
221221
}
222222

223-
function indexOf(array, target, method) {
224-
let index = -1;
225-
// hashes are added to the end of the event array
226-
// so it makes sense to start searching at the end
227-
// of the array and search in reverse
228-
for (let i = array.length - 3; i >= 0; i -= 3) {
229-
if (target === array[i] && method === array[i + 1]) {
230-
index = i;
231-
break;
232-
}
233-
}
234-
return index;
235-
}
236-
237-
function accumulateListeners(obj, eventName, otherActions, meta) {
238-
let actions = meta.matchingListeners(eventName);
239-
if (actions === undefined) { return; }
240-
let newActions = [];
241-
242-
for (let i = actions.length - 3; i >= 0; i -= 3) {
243-
let target = actions[i];
244-
let method = actions[i + 1];
245-
let actionIndex = indexOf(otherActions, target, method);
246-
247-
if (actionIndex === -1) {
248-
let flags = actions[i + 2];
249-
otherActions.push(target, method, flags);
250-
newActions.push(target, method, flags);
251-
}
252-
}
253-
254-
return newActions;
255-
}
256-
257223
function notifyObservers(obj, keyName, meta) {
258224
if (meta.isSourceDestroying()) { return; }
259225

260226
if (deferred > 0) {
261-
let listeners = observerSet.add(obj, keyName, keyName);
262-
accumulateListeners(obj, keyName, listeners, meta);
227+
observerSet.add(obj, keyName);
263228
} else {
264229
sendEvent(obj, keyName, [obj, keyName]);
265230
}

packages/ember-metal/tests/observer_test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -668,9 +668,9 @@ testBoth('observers added/removed during changeProperties should do the right th
668668
assert.equal(removedBeforeFirstChangeObserver.didChangeCount, 0, 'removeObserver called before the first change sees none');
669669
assert.equal(addedBeforeFirstChangeObserver.didChangeCount, 1, 'addObserver called before the first change sees only 1');
670670
assert.equal(addedAfterFirstChangeObserver.didChangeCount, 1, 'addObserver called after the first change sees 1');
671-
assert.equal(addedAfterLastChangeObserver.didChangeCount, 0, 'addObserver called after the last change sees none');
672-
assert.equal(removedBeforeLastChangeObserver.didChangeCount, 1, 'removeObserver called before the last change still sees 1');
673-
assert.equal(removedAfterLastChangeObserver.didChangeCount, 1, 'removeObserver called after the last change still sees 1');
671+
assert.equal(addedAfterLastChangeObserver.didChangeCount, 1, 'addObserver called after the last change sees 1');
672+
assert.equal(removedBeforeLastChangeObserver.didChangeCount, 0, 'removeObserver called before the last change sees none');
673+
assert.equal(removedAfterLastChangeObserver.didChangeCount, 0, 'removeObserver called after the last change sees none');
674674
});
675675

676676

packages/ember/lib/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ Ember.isEmpty = metal.isEmpty;
8787
Ember.isBlank = metal.isBlank;
8888
Ember.isPresent = metal.isPresent;
8989
Ember.run = metal.run;
90-
Ember._ObserverSet = metal.ObserverSet;
9190
Ember.propertyWillChange = metal.propertyWillChange;
9291
Ember.propertyDidChange = metal.propertyDidChange;
9392
Ember.notifyPropertyChange = metal.notifyPropertyChange;

packages/ember/tests/reexports_test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ let allExports =[
7272
['isPresent', 'ember-metal'],
7373
['_Backburner', 'backburner', 'default'],
7474
['run', 'ember-metal'],
75-
['_ObserverSet', 'ember-metal', 'ObserverSet'],
7675
['propertyWillChange', 'ember-metal'],
7776
['propertyDidChange', 'ember-metal'],
7877
['notifyPropertyChange', 'ember-metal'],

0 commit comments

Comments
 (0)