Skip to content

Commit

Permalink
add dependency tracking and caching for computed properties
Browse files Browse the repository at this point in the history
  • Loading branch information
kbrsh committed Mar 24, 2017
1 parent 7808294 commit 3669059
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 7 deletions.
46 changes: 43 additions & 3 deletions dist/moon.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,42 @@
*/
var initComputed = function (instance, computed) {
var setComputedProperty = function (prop) {
// Create dependency map
instance.$observer.dep.map[prop] = [];

// Create Getters/Setters
var properties = {
get: function () {
return computed[prop].get.call(instance);
// Property Cache
var cache = null;
// Any Dependencies of Computed Property
var deps = instance.$observer.dep.map[prop];
// If the computed property has changed
var changed = true;

// Iterate through dependencies, and see if any have been changed
for (var i = 0; i < deps.length; i++) {
changed = instance.$observer.dep.changed[deps[i]];
if (changed) {
break;
}
}

if (changed) {
// Dependencies changed, recalculate dependencies, cache the output, and return it
instance.$observer.dep.target = prop;
cache = computed[prop].get.call(instance);
instance.$observer.cache[prop] = cache;
instance.$observer.dep.target = null;
} else {
// Dependencies didn't change, return cached value
cache = instance.$observer.cache[prop];
}

return cache;
}
};

if (computed[prop].set) {
properties.set = function (val) {
return computed[prop].set.call(instance, val);
Expand All @@ -60,9 +90,15 @@
function Observer(instance) {
this.instance = instance;
this.cache = {};
this.dep = {
target: null,
map: {},
changed: {}
};
}

Observer.prototype.notify = function () {
Observer.prototype.notify = function (key) {
this.dep.changed[key] = true;
queueBuild(this.instance);
};

Expand Down Expand Up @@ -93,6 +129,7 @@
instance.$queued = true;
setTimeout(function () {
instance.build();
instance.$observer.dep.changed = {};
callHook(instance, 'updated');
instance.$queued = false;
}, 0);
Expand Down Expand Up @@ -1169,6 +1206,9 @@
* @return {String} Value of key in data
*/
Moon.prototype.get = function (key) {
if (this.$observer.dep.target) {
this.$observer.dep.map[this.$observer.dep.target].push(key);
}
return this.$data[key];
};

Expand All @@ -1179,7 +1219,7 @@
*/
Moon.prototype.set = function (key, val) {
resolveKeyPath(this, this.$data, key, val);
this.$observer.notify();
this.$observer.notify(key);
};

/**
Expand Down
2 changes: 1 addition & 1 deletion dist/moon.min.js

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion src/instance/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
* @return {String} Value of key in data
*/
Moon.prototype.get = function(key) {
if(this.$observer.dep.target) {
this.$observer.dep.map[this.$observer.dep.target].push(key);
}
return this.$data[key];
}

Expand All @@ -16,7 +19,7 @@ Moon.prototype.get = function(key) {
*/
Moon.prototype.set = function(key, val) {
resolveKeyPath(this, this.$data, key, val);
this.$observer.notify();
this.$observer.notify(key);
}

/**
Expand Down
32 changes: 31 additions & 1 deletion src/observer/computed.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,42 @@
*/
var initComputed = function(instance, computed) {
var setComputedProperty = function(prop) {
// Create dependency map
instance.$observer.dep.map[prop] = [];

// Create Getters/Setters
var properties = {
get: function() {
return computed[prop].get.call(instance)
// Property Cache
var cache = null;
// Any Dependencies of Computed Property
var deps = instance.$observer.dep.map[prop];
// If the computed property has changed
var changed = true;

// Iterate through dependencies, and see if any have been changed
for(var i = 0; i < deps.length; i++) {
changed = instance.$observer.dep.changed[deps[i]];
if(changed) {
break;
}
}

if(changed) {
// Dependencies changed, recalculate dependencies, cache the output, and return it
instance.$observer.dep.target = prop;
cache = computed[prop].get.call(instance);
instance.$observer.cache[prop] = cache;
instance.$observer.dep.target = null;
} else {
// Dependencies didn't change, return cached value
cache = instance.$observer.cache[prop];
}

return cache;
}
};

if(computed[prop].set) {
properties.set = function(val) {
return computed[prop].set.call(instance, val);
Expand Down
8 changes: 7 additions & 1 deletion src/observer/observer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
function Observer(instance) {
this.instance = instance;
this.cache = {};
this.dep = {
target: null,
map: {},
changed: {}
};
}

Observer.prototype.notify = function() {
Observer.prototype.notify = function(key) {
this.dep.changed[key] = true;
queueBuild(this.instance);
}
1 change: 1 addition & 0 deletions src/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var queueBuild = function(instance) {
instance.$queued = true;
setTimeout(function() {
instance.build();
instance.$observer.dep.changed = {};
callHook(instance, 'updated');
instance.$queued = false;
}, 0);
Expand Down

0 comments on commit 3669059

Please sign in to comment.