Skip to content

Commit e316e00

Browse files
committed
Merge pull request #1306 from DAB0mB/fix/double-stop
fix(autorun): destroy event listener once autorun has been stopped
2 parents fa20912 + 2ae0c22 commit e316e00

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

src/modules/core.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ angular.module(name, [
3434
}
3535

3636
const computation = Tracker.autorun(fn, options);
37-
this.$$autoStop(computation);
37+
// Reset to a function that will also stop the listener we just added
38+
computation.stop = this.$$autoStop(computation);
3839
return computation;
3940
};
4041

@@ -89,8 +90,20 @@ angular.module(name, [
8990
return Meteor.apply(...args, fn);
9091
};
9192

93+
// Stops a process once the scope has been destroyed
9294
$$Core.$$autoStop = function(stoppable) {
93-
this.$on('$destroy', stoppable.stop.bind(stoppable));
95+
let removeListener;
96+
const baseStop = stoppable.stop.bind(stoppable);
97+
98+
// Once the process has been stopped the destroy event listener will be removed
99+
// to avoid memory leaks and unexpected behaviours
100+
const stop = (...args) => {
101+
removeListener();
102+
return baseStop(...args);
103+
};
104+
105+
removeListener = this.$on('$destroy', stop);
106+
return stop;
94107
};
95108

96109
// Digests scope only if there is no phase at the moment

tests/integration/core.spec.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,34 @@ describe('angular-meteor.core', function() {
2626
});
2727

2828
it('should call Tracker.autorun()', function() {
29-
var stoppable = { stop: jasmine.createSpy('stop') };
29+
var stop = jasmine.createSpy('stop');
30+
var stoppable = { stop: stop };
3031
spyOn(Tracker, 'autorun').and.returnValue(stoppable);
3132

3233
scope.autorun(function() {});
3334
expect(Tracker.autorun).toHaveBeenCalled();
3435
});
3536

3637
it('should autostop computation', function() {
37-
var stoppable = { stop: jasmine.createSpy('stop') };
38+
var stop = jasmine.createSpy('stop');
39+
var stoppable = { stop: stop };
3840
spyOn(Tracker, 'autorun').and.returnValue(stoppable);
3941

4042
scope.autorun(angular.noop);
4143
scope.$destroy();
4244

43-
expect(stoppable.stop).toHaveBeenCalled();
45+
expect(stop).toHaveBeenCalled();
4446
});
4547

4648
it('should stop computation manually', function() {
47-
var stoppable = { stop: jasmine.createSpy('stop') };
49+
var stop = jasmine.createSpy('stop');
50+
var stoppable = { stop: stop };
4851
spyOn(Tracker, 'autorun').and.returnValue(stoppable);
4952

5053
var computation = scope.autorun(angular.noop);
5154
computation.stop();
5255

53-
expect(stoppable.stop).toHaveBeenCalled();
56+
expect(stop).toHaveBeenCalled();
5457
});
5558

5659
it('should call autorun function using view model as context', function() {
@@ -66,6 +69,18 @@ describe('angular-meteor.core', function() {
6669
scope.autorun(angular.noop);
6770
expect(scope.$digest).toHaveBeenCalled();
6871
});
72+
73+
it('should remove the destroy event listener once the computation has been stopped', function() {
74+
var stop = jasmine.createSpy('stop');
75+
var stoppable = { stop: stop };
76+
spyOn(Tracker, 'autorun').and.returnValue(stoppable);
77+
78+
var computation = scope.autorun(angular.noop);
79+
computation.stop();
80+
scope.$destroy();
81+
82+
expect(stop.calls.count()).toEqual(1);
83+
});
6984
});
7085

7186
describe('subscribe()', function() {
@@ -87,23 +102,25 @@ describe('angular-meteor.core', function() {
87102
});
88103

89104
it('should autostop subscription', function() {
90-
var stoppable = { stop: jasmine.createSpy('stop') };
105+
var stop = jasmine.createSpy('stop');
106+
var stoppable = { stop: stop };
91107
spyOn(Tracker, 'autorun').and.returnValue(stoppable);
92108

93109
scope.subscribe('test');
94110
scope.$destroy();
95111

96-
expect(stoppable.stop).toHaveBeenCalled();
112+
expect(stop).toHaveBeenCalled();
97113
});
98114

99115
it('should stop subscription manually', function() {
100-
var stoppable = { stop: jasmine.createSpy('stop') };
116+
var stop = jasmine.createSpy('stop');
117+
var stoppable = { stop: stop };
101118
spyOn(Tracker, 'autorun').and.returnValue(stoppable);
102119

103120
var subscription = scope.subscribe('test');
104121
subscription.stop();
105122

106-
expect(stoppable.stop).toHaveBeenCalled();
123+
expect(stop).toHaveBeenCalled();
107124
});
108125

109126
it('should return subscription ready and subscriptionId properties', function(done) {

0 commit comments

Comments
 (0)