web-dev-qa-db-fra.com

Javascript setInterval et la solution `this`

J'ai besoin d'accéder à this à partir de mon gestionnaire setInterval

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
        }
    }

Comment puis-je accéder à this.prefs dans ajax.onload?

57
Pablo

La ligne setInterval devrait ressembler à ceci: -

 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 : Le même principe s’applique à "onload". Dans ce cas, son code commun pour le code "externe" a peu d'effet, il configure simplement la demande et l'envoie ensuite. Dans ce cas, la surcharge supplémentaire liée à une fonction supplémentaire, comme dans le code ci-dessus, est inutile. Votre taux de récupération devrait ressembler davantage à ceci: -

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);
}
83
AnthonyWJones
this.intervalID = setInterval(this.retrieve_rate.bind(this), this.INTERVAL);
71
Nechehin

Le comportement par défaut de setInterval consiste à se connecter au contexte global. Vous pouvez appeler une fonction membre en enregistrant une copie du contexte actuel. Dans retrieve_rate, la variable this sera correctement liée au contexte d'origine. Voici à quoi ressemblerait votre code:

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

Conseil bonus: pour une référence de fonction simple (par opposition à une référence d'objet ayant une fonction membre), vous pouvez modifier le contexte en utilisant les méthodes call ou apply de JavaScript.

18
Joel Fillmore

Grâce à l’amélioration de la prise en charge du navigateur, il est maintenant temps d’utiliser l’amélioration EcmaScript 6, méthode de la flèche => , pour conserver this correctement.

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

L'utilisation de la méthode => préserve la this lorsque retrieve_rate() est appelé par l'intervalle. Pas besoin de funky ou de passer this dans les paramètres

10
Martlark

Ce serait la solution la plus propre, car la plupart du temps, vous souhaiterez réellement changer le contexte this pour vos appels de méthode consécutifs:

En outre, il est plus facile de saisir le concept de.

    // 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);
1
Dbl

Avec les navigateurs modernes, la méthode setInterval autorise des paramètres supplémentaires qui sont transmis à la fonction spécifiée par func à l'expiration du délai.

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

Par conséquent, une solution possible peut être:

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

Une démo:

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>

0
gaetanoM
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
        }
    }
0