Javascript setInterval and `this` solution

JavascriptThisSetinterval

Javascript Problem Overview


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.com', true);
        ajax.onload = function()
        {
            // access prefs here
        }
    }

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

Javascript Solutions


Solution 1 - Javascript

this.intervalID = setInterval(this.retrieve_rate.bind(this), this.INTERVAL);

Solution 2 - Javascript

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.com', true);
    ajax.onreadystatechanged= function()
    {
        if (ajax.readyState == 4 && ajax.status == 200)
        {
            // prefs available as self.prefs
        }
    }
    ajax.send(null);
}

Solution 3 - Javascript

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.

Solution 4 - Javascript

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

Solution 5 - Javascript

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

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

Solution 6 - Javascript

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);

Solution 7 - Javascript

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>

Solution 8 - Javascript

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.com', true);
        var context = this;
        ajax.onload = function()
        {
            // access prefs using context.
            // e.g. context.prefs
        }
    }

Solution 9 - Javascript

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

var self = this;
var ajax = null;
//...
ajax.onload = function() {
    self.prefs....;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPabloView Question on Stackoverflow
Solution 1 - JavascriptNechehinView Answer on Stackoverflow
Solution 2 - JavascriptAnthonyWJonesView Answer on Stackoverflow
Solution 3 - JavascriptJoel FillmoreView Answer on Stackoverflow
Solution 4 - JavascriptMartlarkView Answer on Stackoverflow
Solution 5 - JavascriptDanny ApostolovView Answer on Stackoverflow
Solution 6 - JavascriptDblView Answer on Stackoverflow
Solution 7 - JavascriptgaetanoMView Answer on Stackoverflow
Solution 8 - JavascriptMatthew FlaschenView Answer on Stackoverflow
Solution 9 - JavascriptCrozinView Answer on Stackoverflow