Recientemente estoy trabajando en un pequeño módulo de chat, que requiere verificar continuamente el servidor en busca de nuevos mensajes.

Estoy enviando una solicitud ajax a un servidor, y el servidor retiene la conexión hasta que se encuentra un nuevo mensaje (sondeo largo).

Código:

var chatController = function(){

//other variable declaration

/**
*  Ajax call to monitor the new message , on complete of ajax call sending other call
*/

this.checkNewMessage = function(){
  console.log(this); // placed this for debugging purpose
       $.ajax({
         url : SITEURL.CHECK_MESSAGE,
         data : this.currrentUserDetails,
         dataType : 'json' ,
         cache    : false,
         success  :(function(obj){
            //temp = obj;
            return obj.parseNewMessageResponse;
         })(this),
         complete: (function(obj){
            //temp = obj;
            return obj.checkNewMessage;
         })(this),  
       });


};

  // other function and variable

});

Cuando intenté llamar

var mainController = new chatController();
mainController.checkNewMessage();

Problema

Lo que pensé fue que podría enviar una solicitud única continua al servidor, pero para mi sorpresa, solo pude enviar 2 solicitudes ajax una tras otra.

Mi depuración

Cuando intenté depurar, rastreé que para la primera llamada al objeto this que se pasaba apuntaba al chatController

         complete: (function(obj){
            return obj.checkNewMessage;
         })(this),     // this here point to chatController object

Por segunda vez, el objeto this que se pasa apunta al ajax object

         complete: (function(obj){
            return obj.checkNewMessage;
         })(this),     // this here point to ajax object

Estoy usando el cierre de JavaScript para pasar el objeto chatController al parámetro complete de jquery

Entonces, lo que quiero es pasar el parámetro a la función jQuery complete para que sea mi referencia original

2
sanjeev 3 sep. 2014 a las 10:04

3 respuestas

La mejor respuesta

Hay varias soluciones

Puede usar $.proxy :

En mi opinión, la mejor práctica.

$.ajax({
    //...
    success: $.proxy(function(json) {
         // `this` refers to the second argument of `$.proxy`
    }, this)
});

Puede configurar la opción context:

$.ajax({
    //...
    context: this,
    success: function(json) {
         // `this` refers to the value of `context`
    }
});

O utilice un cierre:

var self = this;
$.ajax({
    //...
    success: function(json) {
         // `this` refers to ajax request, use self instead
         $(self).dosomething();
    }
});
6
Community 23 may. 2017 a las 12:07

Hay al menos cuatro formas diferentes de resolver el problema de no llamar a su método con el contexto correcto desde sus controladores success y complete.

  1. Use el argumento context para $.ajax() para que this en su controlador de éxito sea lo que quiere que sea, para que luego pueda llamar a su método.

  2. Use .bind() para crear un nuevo código auxiliar de función que llame a su método con el contexto correcto.

  3. Guarde el valor de this en una variable local para que pueda hacer referencia a esa variable cuando la necesite en una función de código auxiliar.

  4. Utilice la versión de jQuery para varios navegadores de .bind() que se llama $.proxy().

Te ofreceré algunos ejemplos de cada uno.

Primero, la opción context para $.ajax():

this.checkNewMessage = function(){
  console.log(this); // placed this for debugging purpose
       $.ajax({
         context: this,
         url : SITEURL.CHECK_MESSAGE,
         data : this.currrentUserDetails,
         dataType : 'json' ,
         cache    : false,
         success  : function(data) {
             this.parseNewMessageResponse(data);
         },
         complete : function(data) {
             this.checkNewMessage();
         }
       });
};

Luego, usando .bind().

this.checkNewMessage = function(){
  console.log(this); // placed this for debugging purpose
       $.ajax({
         url : SITEURL.CHECK_MESSAGE,
         data : this.currrentUserDetails,
         dataType : 'json' ,
         cache    : false,
         success  : this.parseNewMessageResponse.bind(this),
         complete : this.checkNewMessage.bind(this)
       });
};

Luego, usando una copia guardada de this:

this.checkNewMessage = function(){
     var self = this;
     console.log(this); // placed this for debugging purpose
       $.ajax({
         url : SITEURL.CHECK_MESSAGE,
         data : this.currrentUserDetails,
         dataType : 'json' ,
         cache    : false,
         success  : function(data) {
             self.parseNewMessageResponse(data);
         },
         complete : function(data) {
             self.checkNewMessage();
         }
       });
};

Y finalmente con `.proxy () de jQuery:

this.checkNewMessage = function(){
  console.log(this); // placed this for debugging purpose
       $.ajax({
         url : SITEURL.CHECK_MESSAGE,
         data : this.currrentUserDetails,
         dataType : 'json' ,
         cache    : false,
         success  : $.proxy(this.parseNewMessageResponse, this),
         complete : $.proxy(this.checkNewMessage, this)
       });
};

Si no necesita soporte para IE8 o está bien con la instalación de un polyfill para .bind(), entonces la opción .bind() es mi favorita porque parece la más limpia.

2
jfriend00 3 sep. 2014 a las 06:45

La forma más fácil de resolver esto es definir una referencia a su this original para que pueda acceder desde otro contexto. Mira este sencillo ejemplo:

(function(){
  var _self = this;

  function changeColor($element, color){
    $element.css("background-color", color)
  }

  $(".recolor-btn").click(function(){
    var self = this;
    $.ajax({
      url: "/Color/GetRandom",
      success: function(color){
        _self.changeColor($(self), color);
      }
    });
  });

})();
1
ngergo6 3 sep. 2014 a las 06:21