Skip to content

Commit

Permalink
More optimization/reorganization of computed.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbest committed Jan 3, 2012
1 parent 4ae3405 commit eee91a1
Showing 1 changed file with 40 additions and 46 deletions.
86 changes: 40 additions & 46 deletions src/subscribables/dependentObservable.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,51 @@
function prepareOptions(evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
if (evaluatorFunctionOrOptions && typeof evaluatorFunctionOrOptions == "object") {
ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
var _latestValue,
_hasBeenEvaluated = false,
readFunction = evaluatorFunctionOrOptions;

if (readFunction && typeof readFunction == "object") {
// Single-parameter syntax - everything is on this "options" param
options = evaluatorFunctionOrOptions;
options = readFunction;
readFunction = options["read"];
} else {
// Multi-parameter syntax - construct the options according to the params passed
options = options || {};
options["read"] = evaluatorFunctionOrOptions || options["read"];
if (!readFunction)
readFunction = options["read"];
}
// By here, "options" is always non-null

if (typeof options["read"] != "function")
if (typeof readFunction != "function")
throw "Pass a function that returns the value of the dependentObservable";

options["owner"] = evaluatorFunctionTarget || options["owner"];

return options;
}

ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
var _latestValue,
_hasBeenEvaluated = false,
options = prepareOptions(evaluatorFunctionOrOptions, evaluatorFunctionTarget, options);
var writeFunction = options["write"];
if (!evaluatorFunctionTarget)
evaluatorFunctionTarget = options["owner"];

// Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values
// (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
// plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
var disposeWhenNodeIsRemoved = (typeof options["disposeWhenNodeIsRemoved"] == "object") ? options["disposeWhenNodeIsRemoved"] : null;
var disposeWhenNodeIsRemovedCallback = null;
if (disposeWhenNodeIsRemoved) {
disposeWhenNodeIsRemovedCallback = function() { dependentObservable.dispose() };
ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, disposeWhenNodeIsRemovedCallback);
var existingDisposeWhenFunction = options["disposeWhen"];
options["disposeWhen"] = function () {
return (!ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved))
|| ((typeof existingDisposeWhenFunction == "function") && existingDisposeWhenFunction());
}
}

var _subscriptionsToDependencies = [];
function disposeAllSubscriptionsToDependencies() {
ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
subscription.dispose();
});
_subscriptionsToDependencies = [];
}
var dispose = disposeAllSubscriptionsToDependencies;

// Build "disposeWhenNodeIsRemoved" and "disposeWhenNodeIsRemovedCallback" option values
// (Note: "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
// plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.)
var disposeWhenNodeIsRemoved = (typeof options["disposeWhenNodeIsRemoved"] == "object") ? options["disposeWhenNodeIsRemoved"] : null;
var disposeWhen = options["disposeWhen"] || function() { return false; };
if (disposeWhenNodeIsRemoved) {
dispose = function() {
ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, arguments.callee);
disposeAllSubscriptionsToDependencies();
};
ko.utils.domNodeDisposal.addDisposeCallback(disposeWhenNodeIsRemoved, dispose);
var existingDisposeWhenFunction = disposeWhen;
disposeWhen = function () {
return !ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) || existingDisposeWhenFunction();
}
}

var evaluationTimeoutInstance = null;
function evaluatePossiblyAsync() {
Expand All @@ -59,11 +61,9 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
// Don't dispose on first evaluation, because the "disposeWhen" callback might
// e.g., dispose when the associated DOM element isn't in the doc, and it's not
// going to be in the doc until *after* the first evaluation
if ((_hasBeenEvaluated) && typeof options["disposeWhen"] == "function") {
if (options["disposeWhen"]()) {
dependentObservable.dispose();
return;
}
if (_hasBeenEvaluated && disposeWhen()) {
dispose();
return;
}

try {
Expand All @@ -76,8 +76,7 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
_subscriptionsToDependencies.push(subscribable.subscribe(evaluatePossiblyAsync));
});

var valueForThis = options["owner"]; // If undefined, it will default to "window" by convention. This might change in the future.
var newValue = options["read"].call(valueForThis);
var newValue = readFunction.call(evaluatorFunctionTarget);

for (var oldPos = 0, i = 0, len = oldSubscriptions.length; i < len; i++) {
if (oldSubscriptions[i])
Expand Down Expand Up @@ -105,10 +104,9 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
}

dependentObservable.set = function() {
if (typeof options["write"] === "function") {
if (typeof writeFunction === "function") {
// Writing a value
var valueForThis = options["owner"]; // If undefined, it will default to "window" by convention. This might change in the future.
options["write"].apply(valueForThis, arguments);
writeFunction.apply(evaluatorFunctionTarget, arguments);
} else {
throw "Cannot write a value to a dependentObservable unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.";
}
Expand All @@ -122,13 +120,9 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
return _latestValue;
}

dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; }
dependentObservable.getDependenciesCount = function () { return _subscriptionsToDependencies.length; };
dependentObservable.hasWriteFunction = typeof options["write"] === "function";
dependentObservable.dispose = function () {
if (disposeWhenNodeIsRemoved)
ko.utils.domNodeDisposal.removeDisposeCallback(disposeWhenNodeIsRemoved, disposeWhenNodeIsRemovedCallback);
disposeAllSubscriptionsToDependencies();
};
dependentObservable.dispose = function () { dispose(); };

ko.subscribable.call(dependentObservable);
ko.utils.extend(dependentObservable, ko.dependentObservable['fn']);
Expand Down

0 comments on commit eee91a1

Please sign in to comment.