What is difference on ko.computed between deferEvaluation and extend({deferred: true})
Asked Answered
S

2

6

I am little bit confused by behavior explanation of deferred calculation defined on ko.computed variable.

Such computed variable might be defined with property deferEvaluation: true which should postpone evaluation to the moment when any other property ask for variable value (see http://knockoutjs.com/documentation/computed-reference.html),

When regular ko.computed variable is extended by extend({deferred: true}) it calls calculation asynchronously and postpone it until all currently running "threads" finishes (see http://knockoutjs.com/documentation/deferred-updates.html).

These two settings sound very similar, but each do something completely different.

Can anybody confirm to me that I am right or explain the difference if I am mistaken?

Silici answered 31/1, 2017 at 14:16 Comment(0)
R
6

deferEvaluation is only about deferring initial evaluation. Normally when you create a computed, its evaluator is called right then, synchronously, which means (amongst other things) that all of the observables it depends on must already be initialized. Using deferEvaluation prevents that, and makes the first call to the computed's evalutor function happen the first time anything subscribes to the computed (or never, if nothing ever does). After that, it has no further effect; the computed is still re-evaluated every time anything it depends on changes, immediately.

.extend({deferred: true}) is about always deferring execution of the evaluator until after the task that created or changed it completes (and then typically before the next UI update). It's not just about the initial evaluation, it means every time the observables the computed depends on change, those changes are allowed to settle by making the computed's evaluation asynchronous (after the current task) and waiting for them to stabilize.

Here's a snippet showing using neither of them, using deferEvaluation and using .extend({deferred: true}). Note the differences in when the evaluators are called.

setTimeout(function() {
  console.log("---- Using neither:");
  var ob1 = ko.observable(10);
  console.log("creating c1");
  var c1 = ko.computed(function() {
    console.log("c1 evaluated");
    return ob1() * 2;
  });
  console.log("Setting ob1 to 20");
  ob1(20);
  console.log("subscribing to c1");
  c1.subscribe(function(newValue) {
    console.log("c1's new value is " + newValue);
  });
  console.log("Setting ob1 to 30");
  ob1(30);
  console.log("Setting ob1 to 40");
  ob1(40);
}, 50);

setTimeout(function() {
  console.log("---- Using .extend({deferEvaluation: true}):");
  var ob2 = ko.observable(10);
  console.log("creating c2");
  var c2 = ko.computed(function() {
    console.log("c2 evaluated");
    return ob2() * 2;
  }, null, { deferEvaluation: true });
  console.log("Setting ob2 to 20");
  ob2(20);
  console.log("subscribing to c2");
  c2.subscribe(function(newValue) {
    console.log("c2's new value is " + newValue);
  });
  console.log("Setting ob2 to 30");
  ob2(30);
  console.log("Setting ob2 to 40");
  ob2(40);
}, 200);

setTimeout(function() {
  console.log("---- Using .extend({deferred: true}):");
  var ob3 = ko.observable(10);
  console.log("creating c3");
  var c3 = ko.computed(function() {
    console.log("c3 evaluated");
    return ob3() * 2;
  }).extend({ deferred: true });
  console.log("Setting ob3 to 20");
  ob3(20);
  console.log("subscribing to c3");
  c3.subscribe(function(newValue) {
    console.log("c3's new value is " + newValue);
  });
  console.log("Setting ob3 to 30");
  ob3(30);
  console.log("Setting ob3 to 40");
  ob3(40);
}, 400);
.as-console-wrapper {
  max-height: 100% !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
Riggall answered 31/1, 2017 at 14:44 Comment(0)
R
2

deferEvaluation will prevent the computed from determining its initial value (and dependencies) until something actually accesses it. That could be programmatic (accessing the computed in code) or via a binding. It can be useful for "chicken and egg" scenarios where the computed's code needs to wait for other things to be setup before running or for efficiency if the computed will be triggered several times in initialization before its value is needed/read. The pureComputed functionality can accomplish the same thing with additional benefits over the life of the computed (http://knockoutjs.com/documentation/computed-pure.html).

extend({deferred: true}) will opt the observable/observableArray/computed into the deferred updates functionality. Deferred updates means that you can synchronously make multiple updates to an observable/observableArray or to the dependencies of a computed and it will only trigger a single change (which happens asynchronously). This can have be very beneficial to a complex UI, as you will avoid multiple re-renders for intermediate changes.

If I was starting a new application, I would turn on deferred updates (ko.options.deferUpdates = true) and use ko.pureComputed as much as possible for the best performance and memory usage.

Roulade answered 31/1, 2017 at 14:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.