80

I need to access this from my setInterval handler

prefs: null,
startup : function()
    {
        // init prefs
        ...
        this.retrieve_rate();
        this.intervalID = setInterval(this.retrieve_rate, this.INTERVAL);
    },

retrieve_rate : function()
    {
        var ajax = null;
        ajax = new XMLHttpRequest();
        ajax.open('GET', 'http://xyz.example', true);
        ajax.onload = function()
        {
            // access prefs here
        }
    }

How can I access this.prefs in ajax.onload?

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Pablo
  • 28,133
  • 34
  • 125
  • 215

9 Answers9

110
this.intervalID = setInterval(this.retrieve_rate.bind(this), this.INTERVAL);
Nechehin
  • 1,351
  • 1
  • 10
  • 10
  • 17
    This is the right solution. The accepted solution seems to require unnecessarily more code. – Nick May 22 '14 at 23:37
  • 5
    But this method has a drawback. Most likely, it will not work with older versions of IE – Nechehin Jun 11 '14 at 12:25
  • 2
    @Nechehin Worth noting. But its still a much cleaner solution. – connorbode Mar 11 '15 at 00:10
  • 2
    It's supported since IE9 so is a clean solution for me. – barbara.post Apr 20 '15 at 08:37
  • 2
    If you need support for IE8 *and* you're using [Underscore.js](http://underscorejs.org), you could use [`_.bind`](http://underscorejs.org/#bind): `this.intervalID = setInterval(_.bind(this.retrieve_rate, this), this.INTERVAL);` – gfullam Aug 21 '15 at 15:44
95

The setInterval line should look like this:-

 this.intervalID = setInterval(
     (function(self) {         //Self-executing func which takes 'this' as self
         return function() {   //Return a function in the context of 'self'
             self.retrieve_rate(); //Thing you wanted to run as non-window 'this'
         }
     })(this),
     this.INTERVAL     //normal interval, 'this' scope not impacted here.
 );

Edit: The same principle applies to the " onload ". In this case its common for the "outer" code to do little, it just sets up the request an then sends it. In this case the extra overhead an additinal function as in the above code is unnecessary. Your retrieve_rate should look more like this:-

retrieve_rate : function()
{
    var self = this;
    var ajax = new XMLHttpRequest();
    ajax.open('GET', 'http://xyz.example', true);
    ajax.onreadystatechanged= function()
    {
        if (ajax.readyState == 4 && ajax.status == 200)
        {
            // prefs available as self.prefs
        }
    }
    ajax.send(null);
}
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
  • I was going to do this initially, but then I remembered this pattern is really most useful for loops. – Matthew Flaschen May 01 '10 at 08:26
  • @Matthew Flaschen: It just as useful for this scenario as it is for loops. – Andy E May 01 '10 at 09:51
  • @Anthony: so the trick with `self` is the only option here? can you confirm that the solution by Matthew will not work? – Pablo May 01 '10 at 13:03
  • @Michael: First of all its not a "trick" its just how things work in Javascript. Matthew's answer as it currently stands at time of writing this comment doesn't work. There was an earlier version of it that might have worked but it involved passing `this` as a parameter which was unnecessary and awkard (any caller of `retrieve_rate` would have know this unnecessary special requirement). – AnthonyWJones May 01 '10 at 19:26
  • 1
    passing `this` as an argument to the `(function(self){...})(this)` in setInterval didn't work for me because the function is executed immediately instead of being delayed. @Joel Fillmore's solution works for me – Homan Jan 21 '13 at 18:59
  • I wish I had known about this 12 hours ago. Going to permanently etch it into my brain now. Thanks! – dalemac Apr 18 '14 at 09:44
  • If you're using [Underscore.js](http://underscorejs.org), you could use [`_.bind`](http://underscorejs.org/#bind) which does this for you: `this.intervalID = setInterval(_.bind(this.retrieve_rate, this), this.INTERVAL);` – gfullam Aug 21 '15 at 15:46
  • That's a lot of brackets over there – techkuz Jun 21 '18 at 07:53
  • Why you didn't do in first code snippet same as second code: store the `this` ref in a local var as `var self = this;` and simply use that in `setInterval` ? – S.Serpooshan Jul 25 '21 at 07:37
19

The default behavior of setInterval is to bind to the global context. You can call a member function by saving a copy of the current context. Inside retrieve_rate the this variable will be correctly bound to the original context. Here is what your code would look like:

var self = this;
this.intervalID = setInterval(
    function() { self.retrieve_rate(); },
    this.INTERVAL);

Bonus tip: For a plain function reference (as opposed to an object reference which has a member function) you can change the context by using JavaScript's call or apply methods.

Joel Fillmore
  • 5,978
  • 2
  • 27
  • 18
  • 1
    This worked for me, the call to 'call' doesn't seem to be needed though. The context of retrieve_rate should be set to self by default, because it is called as a member function. – Dreendle Feb 07 '12 at 12:47
  • @Dreendle - you are right, I remembered solving this for a callback function reference where it was needed. I've fixed the answer, thanks! – Joel Fillmore Feb 08 '12 at 16:43
17

With improving browser support the time is now good to use the EcmaScript 6 enhancement, the arrow => method, to preserve this properly.

startup : function()
    {
        // init prefs
        ...
        this.retrieve_rate();
        this.intervalID = setInterval( () => this.retrieve_rate(), this.INTERVAL);
    },

Using => method preserves the this when retrieve_rate() is called by the interval. No need for funky self or passing this in parameters

Pete
  • 1,305
  • 1
  • 12
  • 36
Martlark
  • 14,208
  • 13
  • 83
  • 99
10

window.setInterval(function(){console.log(this)}.bind(this), 100)

this is legal in javascript and saves lots of code :)

Danny Apostolov
  • 479
  • 3
  • 8
3

This would be the cleanest solution, since most of the time you actually want to switch the this context for your consecutive method calls:

Also it's easier to grasp the concept of.

    // store scope reference for our delegating method
    var that = this;
    setInterval(function() {
        // this would be changed here because of method scope, 
        // but we still have a reference to that
        OURMETHODNAME.call(that);
    }, 200);
Dbl
  • 5,634
  • 3
  • 41
  • 66
3

With modern browsers the setInterval method allows additional parameters which are passed through to the function specified by func once the timer expires.

var intervalID = scope.setInterval(func, delay[, param1, param2, ...]);

Hence, a possible solution can be:

this.intervalID = setInterval(function (self) {
        self.retrieve_rate();
    }, this.INTERVAL, this);

A demo:

var timerId;
document.querySelector('#clickMe').addEventListener('click', function(e) {
    timerId = setInterval(function (self) {
        self.textContent = self.textContent.slice(0, -1);
        if (self.textContent.length == 0) {
            clearInterval(timerId);
            self.textContent = 'end..';
        }
    }, 250, this);
})
<button id="clickMe">ClickMe</button>
gaetanoM
  • 41,594
  • 6
  • 42
  • 61
0
prefs: null,
startup : function()
    {
        // init prefs
        ...
        this.retrieve_rate();
        var context = this;
        this.intervalID = setInterval(function()
                                      {
                                          context.retrieve_rate();
                                      }, this.INTERVAL);
    },

retrieve_rate : function()
    {
        var ajax = null;
        ajax = new XMLHttpRequest();
        ajax.open('GET', 'http://xyz.example', true);
        var context = this;
        ajax.onload = function()
        {
            // access prefs using context.
            // e.g. context.prefs
        }
    }
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539
-1

That's not a beauty solution but it's in common usage:

var self = this;
var ajax = null;
//...
ajax.onload = function() {
    self.prefs....;
}
Crozin
  • 43,890
  • 13
  • 88
  • 135