0

I have a timeout loop like this:

var somedata = {
        autoRefreshInterval: 300,
        autoRefreshInSec: 300,
        myTimeout: null,
        doRefresh: _doRefresh,
        onTimeout: function () {
            this.autoRefreshInSec--;
            if (this.autoRefreshInSec <= 0) {
                this.autoRefreshInSec = this.autoRefreshInterval; 
                this.doRefresh();
            }
            this.myTimeout = $timeout(this.onTimeout, 1000);
        },
        startTimer: function () {
            this.autoRefreshInSec = this.autoRefreshInterval;
            this.myTimeout = $timeout(this.onTimeout, 1000);
        },
        stopTimer: function () {
            $timeout.cancel(this.myTimeout);
        },
    }

It appears that the "this" doesn't work inside of onTimeout callback function, while it works fine for startTimer and stopTimer. How to fix it?

UPDATE:

Since this is lost inside the onTimeout based on one of the answers below, I tried to pass it into like this:

onTimeout: function (self) {
    self.autoRefreshInSec--;
    if (self.autoRefreshInSec <= 0) {
        self.autoRefreshInSec = self.autoRefreshInterval;  // Set it here so that it does't start refresh right away. It will be reset when refresh is done.
        self.doRefresh();
    }
    self.myTimeout = $timeout(self.onTimeout(self), 1000);
},
startTimer: function () {
    this.autoRefreshInSec = this.autoRefreshInterval;
    this.myTimeout = $timeout(this.onTimeout(this), 1000);
},

Strangely, when I debug through the code, it seems working. However, once I removed the break points, self.doRefresh() is fired continuously. Why?

UPDATE 2:

Okay, I created a JSFiddle at http://jsfiddle.net/qY86q/1 to illustrate the problem.

6
  • What does it have to do with AngularJS? Commented Jul 24, 2014 at 22:52
  • $timeout is an AngularJS function, right? Commented Jul 24, 2014 at 23:13
  • Angular has $timeout service. Where do you inject it in your code? Commented Jul 24, 2014 at 23:18
  • $timeout is injected correctly. That's not my question. My question is from inside of onTimeout (which is called by $timeout) how to access something like autoRefreshInSec? "this" (as I showed) is not working. Commented Jul 24, 2014 at 23:55
  • this refers to the function you are in. So inside your $timeout.cancel() it refers to the function $timeout. Just use the name itself. Commented Jul 25, 2014 at 0:04

2 Answers 2

2

Function.prototype.bind()

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Solution for your case

angular.module('myApp', [])
  .service('timerService', function($timeout) {
    var _timer = {
      autoRefreshInterval: 300,
      autoRefreshInSec: 300,
      myTimeout: null,
      onTimeout: function() {
        this.autoRefreshInSec -= 1;
        if (this.autoRefreshInSec <= 0) {
          this.autoRefreshInSec = this.autoRefreshInterval;
          console.log('refreshing');
        }
        console.log('time: ', this.autoRefreshInSec);
        this.myTimeout = $timeout(this.onTimeout.bind(this), 1000);
      },
      startTimer: function() {
        if (this.myTimeout) {
          this.stopTimer(this.myTimeout)
        }
        this.autoRefreshInSec = this.autoRefreshInterval;
        this.myTimeout = $timeout(this.onTimeout.bind(this), 1000);
      },
      stopTimer: $timeout.cancel // see note(1)
    };
    var context = {
      timer: _timer
    };
    return context;
  }).controller('PrefsCtrl', function PrefsCtrl($scope, timerService) {
    $scope.timer = timerService.timer;
  })
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="myApp">
  <div ng-controller="PrefsCtrl">
    <button ng-click="timer.startTimer()">Click to Start or Reset Timer</button>
    <div>{{timer.autoRefreshInSec}}</div>
  </div>
</div>

note(1), this is shorcut for

stopTimer: function(timer) {
   $timeout.cancel(timer)
}
Sign up to request clarification or add additional context in comments.

5 Comments

So, how do I apply your solution to my problem?
It doesn't work. "This" is actually a window object inside onTimeout as I debug through it. This.autoRefreshInSec is undefined.
Inside startTimeout this points to global (window) object – You have implemented some mix of my proposal solution.
Please see my update with a link to JFiddle project. Hope you can fix it there.
Sorry about that. The link to jsfiddle is up-to-date now.
0

This is a javascript binding issue.

Try:

var somedata;  // now this can be referenced in the angular.bind call
somedata = {
    onTimeout: angular.bind(somedata, function () {
        this.autoRefreshInSec--;
        if (this.autoRefreshInSec <= 0) {
            this.autoRefreshInSec = this.autoRefreshInterval; 
            this.doRefresh();
        }
        this.myTimeout = $timeout(this.onTimeout, 1000);
    })
}

which will ensure the this at the time that function is called is "bound" to somedata inside the callback.

2 Comments

ah yes you are correct. Too quick with the response. But it still is a binding issue (nothing magic to angular)
Your updated answer doesn't seem to work either. "this" in this.autoRefreshInSec still points to window.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.