Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to use object oriented javascript and AJAX

I'm trying to use some Object Oriented Javascript to keep my code clean rather than have a giant messy script like usual. Using Ajax is making this complicated, with its Asynchronous nature, its difficult to separate code.

A specific problem I am having is setting object properties with Ajax.

I have the following code:

var Class = function(){
  this.attr1;
}

Class.prototype.setAttr1 = function(){
  var self = this;   
  $.ajax({
    url:'http://api.something.com?get=somedata',
    success: function(data){
      self.attr1 = data.name;
    }
  });
}

This should work, but I can't really be certain that the property is set if I access it from somewhere else, correct? Is there a way to deal with that. Like if I need to use attr1 somewhere, can I wait for the ajax to return or something? Or if I'm totally off, how to people encapsulate code when using Ajax?

like image 967
Dan Avatar asked Jan 09 '23 20:01

Dan


2 Answers

This should work, but I can't really be certain that the property is set if I access it from somewhere else, correct?

Correct

Is there a way to deal with that. Like if I need to use attr1 somewhere, can I wait for the ajax to return or something?

Yup: Where you want to use attr, have the accessor accept a callback (directly, or indirectly by returning a promise), and have it call the callback (or resolve the promise) when the attribute is available.

It looks like you want to keep the code that initiates the ajax call separate from the code that uses the attribute value later. If so, a promise-based mechanism will probably be more useful in this situation. As you're using jQuery, here's a jQuery example using jQuery's Deferred and Promise:

var Class = function(){
  this.attr1LoadDeferred = $.Deferred();
}

Class.prototype.setAttr1 = function(){
  var self = this;   
  $.ajax({
    url:'http://api.something.com?get=somedata',
    success: function(data){
      self.attr1 = data.name;
      self.attr1LoadDeferred.resolveWith(self);
    }
  });
}
Class.prototype.accessAttr1 = function() {
    return this.attr1LoadDeferred.promise();
};

Usage:

// Create an instance
var c = new Class();

// At some point, initiate loading the value
c.setAttr1();

// At some point, look to use the value
c.accessAttr1().then(function(inst) {
    console.log(inst.attr1);
});

// Somewhere else, look to use the value
c.accessAttr1().then(function(inst) {
    doSomethingElseWith(inst.attr1);
});

Unfortunately, jQuery's Deferred/Promise implementation has an issue that most promises libraries don't: The callback you pass then will sometimes be called asynchronously, other times synchronously. (Specifically: If the promise is already resolved, the callback is synchronous.) Either just keep that in mind when using it, or use a different promise implementation.

like image 134
T.J. Crowder Avatar answered Jan 19 '23 13:01

T.J. Crowder


Typically, you would either:

  • Pass around callbacks or promises which would let your code wait for the values to be set; the jQuery framework makes heavy use of callbacks, but also returns promises from its AJAX functions.

  • Have your objects raise events when attributes are changed. Backbone is a great example of a framework which makes heavy use of events.

like image 34
meagar Avatar answered Jan 19 '23 14:01

meagar