How use setTimeout() with Backbone? - javascript

How can I use setTimeout() in a Backbone model?
I have the next code:
var ContentModel = Backbone.Model.extend({
URL: "http://localhost/example.php",
requestType: "POST",
dataType: "json",
data: "", //Set the value outside the model
startSend: function (Data) {
//
},
reply: function (Data) {
var dataJson = eval(Data);
console.log(dataJson);
setTimeout(this.ajaxRequest(),4000);
},
problems: function (Data) {
//
},
ajaxRequest: function () {
$.ajax({
async:true,
type: this.requestType,
dataType: this.dataType,
url: this.URL,
data: this.data,
beforeSend:this.startSend,
success: this.reply,
timeout:4000,
error:this.problems
});
}
});
Alternatively I have tried:
setTimeout(function(){
//ajax code
},4000);
But the result is the same. setTimeout() don't work. The request only run once.

A couple of things are amiss. First off, this line:
setTimeout(this.ajaxRequest(),4000);
Should be:
setTimeout(this.ajaxRequest, 4000);
The first line of code executes the ajaxRequest function and passes the result (which is undefined) to setTimeout. That means the ajaxRequest function will execute once, but too soon. The latter line does what you want, which is to pass the function itself to setTimeout, and ajaxRequest will be called 4 seconds later.
But that's not quite enough. When the ajaxRequest function is executed, the value of this context is incorrect. When you called setTimeout, the context of the callback is set to window. You can verify this by console.log(this) in the callback function.
To fix it, you need to bind the context of the function. Since you're using Backbone, you've also got underscore.js already loaded. Using _.bind oughta do it:
setTimeout(_.bind(this.ajaxRequest, this), 4000);
Edit:
Come to think of it, there might be another problem. When the ajax call succeeds or fails, the reply or problems functions may suffer from the same loss of context as ajaxRequest did. But if there is, it's easily fixed.
Instead of calling _.bind on those too, the simplest way is to call _.bindAll in your Backbone model constructor.
initialize: function() {
_.bindAll(this, 'ajaxRequest', 'reply', 'problems');
}
When you call _.bindAll, underscore guarantees that every time any of the listed methods of your model is called, the this context variable points to the model itself, unless specifically bound to something else.

You don't need to do anything special to use setTimeout with backbone. Check the scope of this in your reply function. My guess it that this.ajaxRequest() isn't in scope.

You have to use setInverval instead.
setInterval(this.ajaxRequest, 4000);
setTimeout Triggers the function once.
setInterval Triggers each n ms.
var interval = setInterval(this.ajaxRequest, 4000);
clearInterval used to clear setInterval.
clearInterval(interval);
Or pass the context parameter on your ajax:
$.ajax({
...
context: this,
...
});

Related

jquery plugin - make ajax call chainable

I am trying to write a jquery plugin.
Now, I would like to use an ajax-call in one of the functions of this plugin.
However it should be chainable, meaning in this case that the ajax-call should be executed and only after something was returned, the return should happen.
I tried different things but so far I could not make it so that the second function in the chain is really executed after the return value of the ajax call was retrieved;
In this version (see below) I can use
$(document).createDatabase().message();
So, chaining works.
But looking at the console log – the message() function is executed before the ajax-call in createDatabase() is finished.
How can I make it so that
$(document).createDatabase().message();
first returns the output/return of the ajax-call that is inside createDatabase() and only after that message() is executed?
Code:
(function($) {
$.fn.createDatabase = function(options) {
// Plugin options defaults
var settings = $.extend({
name: 'NA',
onExit: function() {} // callback function
}, options);
$.ajax({
method: "POST",
url: "plugin/php/createDatabase.php",
data: settings
})
.done(function(msg) {
console.log("createDatabase executed with message: " + msg);
settings.onExit.call();
});
return this;
};
$.fn.message = function() {
console.log('this should appear after the console-log message of createDatabase');
return this;
}
}(jQuery));
Thank You!
You could do a couple of things, but they are all a bad idea. The reason is that these are two separate jQuery plugins - a plugin should be capable of working on its own. There is no guarantee or reason that somebody won't do .message().createDatabase(). If you need to guarantee the order of execution, then you should use just one plugin. One possible way would be to pass message inside options when you call .createDatabase(options), and then run message when the call is done. Like so
$(document).createDatabase({
message: function() {...}
});
...
var promise = $.ajax({
method: "POST",
url: "plugin/php/createDatabase.php",
data: settings
})
.done(function(msg) {
console.log("createDatabase executed with message: " + msg);
settings.onExit.call();
});
...
if ( settings.message )
promise.done( settings.message.bind(this) );
I tried different things but so far I could not make it so that the second function in the chain is really executed after the return value of the ajax call was retrieved;
The second function executes when the first one finishes. The first one finishes with return this, but this happens before the ajax call is done because ajax calls are asynchronous.

How to pass the scope to the callback?

I have the following code structure and I am trying to maintain the scope of "this" to the current object in the success and error callbacks.
I have tried using Apply function, but I am not sure how to use in my use-case and that's where I need some help.
Here is the code I have :
function navigateToDocumentList(record){
var self= this;
var handoutDocumentList=Ext.create('xxx.view.HandoutsDocumentListPanel');
var navigationView = self.getNavigationView();
navigationView.push(handoutDocumentList);
navigationView.getNavigationBar().show();
navigationView.getNavigationBar().setTitle(record.data.name);
var url = intermountain.constants.HANDOUTS_DOCUMENTS_OF_CATEGORY_SERVICE_URL+record.data.name;
self.makeAJAXCallWIthParams(url,
16000,
self.navigateToDocumentListSuccess,
self.navigateToDocumentListError,
self);
}
function makeAJAXCallWithParams(url, timeout, success, error, callbackObj) {
Ext.Viewport.setMasked({
xtype: 'loadmask',
message: 'Loading ...'
});
Ext.Ajax.request({
url: url,
timeout: 5000,
success: success.apply(callbackObj,[]),// I need to know how to pass the function arguments here as earlier I was just calling it by success and now I need to call it using apply function
failure: failure
});
}
Please let me know if I need to explain the problem better. Any input / hint would be appreciated.
Thanks for your time!
To answer your question about arguments, each function in Javasctript can access a list with all its arguments via the arguments pseudo-parameter:
success: function(){ return success.apply(callbackObj, arguments) }
However, its going to be simpler to use the bind method instead of apply or call.
success: success.bind(callbackObj),
failure: failure.bind(callbackObj)

use ajax request json output in settimeout function

I have a var in jquery after a request like
type: 'POST',
data: data,
cache: false,
success: function (data) {
var json = jQuery.parseJSON(data);
I'm trying to use timeout to fire the function below after five seconds.
$("#a" + json.id).fadeOut(300);
At the moment I'm doing
setTimeout('$("#a" + json.id).fadeOut(300)', 500);
but it doesn't seem to work
setTimeout takes a function and a number as a parameter, try this:
setTimeout(function() {
$("#a" + json.id).fadeOut(300);
}, 500);
Not sure if the value of json.id changes by the time the timeout callback is called.
Consider the following example:
for (var i=0;i<10;i++){
setTimeout(function(){console.log(i)},500);
}
The callback function sees i=10 because that's the value of i by the time the function is invoked. You can do a pre-binding by using closure:
var f=function(id){
setTimeout(function(){console.log(id);},500);
}
for (var i=0;i<10;i++) f(i);
Now that you see how closure and pre-binding work, here's a more robust solution to your question:
var f=function(id){
setTimeout(function(){$('#a'+id).fadeOut(300);},500);
}
f(json.id);
Your code doesn't work because the String is eval'ed in global context. So for it to work you can make json global (remove the var).
Also, while I am not sure where you are calling the setTimeout from, but assuming it is inside the callback, you can alternatively make the id a part of the string :
setTimeout('$("#a'+json.id+'").fadeOut(300)', 500);
But certainly a better option is to avoid eval and globals at all costs (checkout Eval is evil and Why global state is the devil for an elaborate explaination) and pass in a closure :
setTimeout(function(){ $("#a" + json.id).fadeOut(300); }, 500);

How to extract ajax response data from the success callback

Sorry if this is a duplicate but I couldn't find any satisfying answers in the previous posts.
$(function() {
$.ajax({
url: 'ajax/test.html',
success: function(data) {
// Data received here
}
});
});
[or]
someFunction() {
return $.ajax({
// Call and receive data
});
}
var myVariable;
someFunction().done(function(data) {
myVariable = data;
// Do stuff with myVariable
});
The above code works just fine. However, this ajax request is made on page load and I want to process this data later on. I know I can include the processing logic inside the callback but I don't want to do that. Assigning the response to a global variable is not working either because of the asynchronous nature of the call.
In both the above ways, the 'data' is confined either to the success callback or the done callback and I want to access it outside of these if possible. This was previously possible with jQuery 'async:false' flag but this is deprecated in jQuery 1.8.
Any suggestions are appreciated. Thank you.
You can "outsource" the callback to a normal function, so you can put it somewhere, you like it:
$(function() {
$.ajax({
url: 'ajax/test.html',
success: yourOwnCallback
});
});
somehwere else you can define your callback
function yourOwnCallback(data) {
// Data received and processed here
}
this is even possible with object methods as well
This solution might not be idea but I hope it helps.
Set the variable upon callback.
Wherever you need to process the data, check if variable is set and if not wait somehow.
Try:
$(document).ready(function(){
var myVar = false;
$.ajax({
url: 'ajax/test.html',
success: function(data) {
myVar=data;
}
});
someFunction(){ //this is invoked when you need processing
while(myVar==false){}
... do some other stuff ..
}
});
Or
someFunction(){
if(myVar==false){
setTimeout(someFunction(),100); //try again in 100ms
return;
}
.. do some other stuff ..
}

Calling JavaScript method in a particular scope

Dear All,
I'm using dojo.declare to create classes in JavaScript. In one of the methods, I've an AJAX request. In the load method of that request, I need to execute certain methods.These methods are actually methods of the class that was created using dojo.declare. I tried to execute the method using this. But it gave me method not found error. So I used dojo.hitch(this,testMethod) to invoke it. It worked fine. Now the problem is I've lot of other methods also inside testMethod() which internally calls other methods of my JavaScript class. It is really a pain to have dojo.hitch() everywhere. Is there any work around for this.
dojo.declare("TestClass",null,{
getData:function(url){
dojo.xhrGet({
url:url,
load: function (response){
dojo.hitch(scope of the current object,testMethod(response))
},
error:function(){
}
});
},
testMethod:function(response){
//calls testMethod2. I think I can use dojo.hitch(this,testMethod3) to call it.
//but I wanted to avoid doing it every time.
},
testMethod2:function(){
//calls testMethod3
},
testMethod3:function(){
//can call other methods.
}
});
It seems like that execution scope was lost in this code:
load: function (response){
dojo.hitch(this,testMethod(response))
},
I made small changes in your code. Now it should work properly.
dojo.declare("TestClass",null,{
getData:function(url){
dojo.xhrGet({
url:url,
load: dojo.hitch(this,this.testMethod),
error:function(){
}
});
},
testMethod:function(response){
this.testMethod2();
},
testMethod2:function(){
this.testMethod3();
},
testMethod3:function(){
//can call other methods.
}
});
This is a typical context problem. You are passing an uncontexted function as a property of a configuration hash, which is passed as argument to dojo.xhrGet.
dojo.hitch is exactly the right construct to add a context to a function. Another way is to simply use a closure. Is there any reason why you can't do:
var me = this;
dojo.xhrGet({
url:url,
load: function(response) {
me.testMethod(response);
}
});
Try doing it like this:
dojo.xhrGet({
url:url,
load: dojo.hitch(this, "testMethod"),
error:function(){
}
});
Your way worked as well, but it saves you a few bytes and is just cleaner to use the method name as a string. Hitch will automatically pass the arguments for you.

Categories