setupSingleBindComputeHandlers leak temporary observables. #1155

Closed
justinbmeyer opened this Issue Jul 8, 2014 · 4 comments

Comments

Projects
None yet
3 participants
@justinbmeyer
Contributor

justinbmeyer commented Jul 8, 2014

If a {{#if}} block binds to something (ex state in {{#if state}}) and another computes updates state like:

var updateCompute = can.compute(function(){
  map.attr("state", "resolved");
  return 1;
});

This causes an infinite loop.

The reason is complex.

Essentially, {{#if state}} sets up a "singleBindComputeHandler" for performance reasons. This assumes the events to listen for never changes. It never calls __read. __read adds a bindSet to the stack. As __read is never called, any read observables are added to the top of the bind stack. This is updateCompute's bind stack.

The fix is to probably use can.__clearReading / __addReading around

https://github.com/bitovi/canjs/blob/master/compute/compute.js#L235

Or, if that damages performance, to simply push a dummy object on the stack and remove it manually.

This will read

The reason is that {{#if}} creates a compute and reads from it

It's possible for a compute function to set a value which will cause an infinite loop.

For example, something like:

var map = new can.Map({count: 0});

var c1 = can.compute(function(){
  return map.attr("count")+1;
});

var c2 = can.compute(function(){
  map.attr("count", map.count+ 1);
  return 1;
});

Essentially, when c2 sets count, this will immediately update c1. This will fire an update of c1 which reads

@justinbmeyer justinbmeyer added the Bug label Jul 8, 2014

@justinbmeyer justinbmeyer modified the milestones: 2.1.4, 2.1.3 Jul 8, 2014

@asavoy

This comment has been minimized.

Show comment
Hide comment
@asavoy

asavoy Jul 9, 2014

Contributor

+1 - I believe I'm running into this.

Contributor

asavoy commented Jul 9, 2014

+1 - I believe I'm running into this.

@alexisabril

This comment has been minimized.

Show comment
Hide comment
@alexisabril

alexisabril Jul 10, 2014

Contributor

Potentially related for components(using if to check an attribute on a parent item's scope): http://jsfiddle.net/b65UW/

Contributor

alexisabril commented Jul 10, 2014

Potentially related for components(using if to check an attribute on a parent item's scope): http://jsfiddle.net/b65UW/

@justinbmeyer

This comment has been minimized.

Show comment
Hide comment
@justinbmeyer

justinbmeyer Jul 24, 2014

Contributor

@asavoy can you share what's breaking for you? I'm unable to recreate this issue. My attempt looked like:

        var map = new can.Map({state: "pending"});
        var source = can.compute(1)
        var number = can.compute(function(){
          map.attr("state", "resolved");
          return source();
        });

        var template = can.stache("{{#map}}<div>{{#if state}}{{number}}{{/if}}</div>{{/map}}")

        var frag = template({
            number: number,
            map: map
        });
        source(2);

        ok(true);
Contributor

justinbmeyer commented Jul 24, 2014

@asavoy can you share what's breaking for you? I'm unable to recreate this issue. My attempt looked like:

        var map = new can.Map({state: "pending"});
        var source = can.compute(1)
        var number = can.compute(function(){
          map.attr("state", "resolved");
          return source();
        });

        var template = can.stache("{{#map}}<div>{{#if state}}{{number}}{{/if}}</div>{{/map}}")

        var frag = template({
            number: number,
            map: map
        });
        source(2);

        ok(true);
@justinbmeyer

This comment has been minimized.

Show comment
Hide comment
@justinbmeyer

justinbmeyer Jul 24, 2014

Contributor

Actually, I found it.

Contributor

justinbmeyer commented Jul 24, 2014

Actually, I found it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment