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);
Related
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,
...
});
I'm experiencing a problem of $.get function.
The url contains JSON
my code:
xyz = null
$.get('http://www.someurl.com/123=json', function(data) {
var xyz = data.positions[0].latitude;
});
alert(xyz);
//some more code using xyz variable
I know that xyz will alert a null result because the $.get is asynchronous.
So is there any way I can use the xyz outside this get function?
get is a shortcut. You can do the same, but synchronous, using:
var xyz = null
$.ajax({ url: 'http://www.someurl.com/123=json',
async: false,
dataType: 'json',
success: function(data) {
xyz = data.positions[0].latitude;
}
});
alert(xyz);
You'll have to declare the xyz variable before the ajax call, though.
The real answer is NO, but you can use this:
function useXYZ(){
alert(xyz);
}
xyz = null
$.get('http://www.someurl.com/123=json', function(data) {
xyz = data.positions[0].latitude;
useXYZ();
});
This is a common issue with Javascript. Javascript code must be written in continuation passing style. Its annoying but its something you can convert without thinking too much.
Basicaly, whenever we would have something like
var x = someSyncFunction(a, b, c);
//do something with x
console.log(x);
We can convert it into async code by making all the code after the function returns into a continuation function and turning x from a variable into a parameter of the continuation callback.
someAsyncFunction(a, b, c, function(x){
//do something with x;
console.log(x);
});
You have to watch out that its very easy to write confusing code. A good trick to keep in mind is taht you can make your own functions also receive callbacks. This allows them to be used by different function (just like normal sync helper functions that return a value can be used by different functions)
var getXyz = function(onResult){ //async functions that return do so via callbacks
//you can also another callback for errors (kind of analogous to throw)
$.get('http://www.someurl.com/123=json', function(data) {
var xyz = data.positions[0].latitude;
onResult(xyz); //instead of writing "return xyz", we pass x to the callback explicitely.
});
};
getXyz(function(xyz){ //this would look like "var xyz = getXyz();" if it were sync code instead.
console.log('got xyz');
});
The trick here is to change all return statements from the function into calls to the callback function. Think as if async function never returned and the only way to give a value back to someone is to pass that value to a callback.
You might ask why there isnt an easier way to do all of this. Well, there is not, unless you use another language instead of Javascript (or at least something that lets you write async code in synchronous style but automatically compiles down to regular Javascript)
I've been working with getters and setters to avoid the prospect of using global variables. However, I've run into a problem. The below code, which works fine with integer variables, throws an exception when I try to run an AJAX call instead. Can someone explain to me why this is happening?
function Object_XML() {
me = this;
me.xml = null;
}
Object_XML.prototype = {
getXML: function() {
return me.xml
},
setXML: function(data) {
me.xml = data;
},
loadXML: function() {
$.ajax({
type: "GET",
url: "questions.xml",
dataType: "xml",
success: function(xml) {
me.setXML(xml);
} //close success
});//close AJAX
}//close setXML
};
$(document).ready(function() {
var data = new Object_XML();
alert("This is an " + data.getXML());
data.setXML();
alert("This is an " + data.getXML());
});
Thanks, Elliot Bonneville
You just negated your use of private variables with getters and setters by using me = this; You just made me a global variable by not using var. (any variable not defined using var gets attached to the global namespace)
In your case since you're working within the same object scope you can just use this and avoid the me as personally, i think it's confusing. But if you want to stick to that paradigm, use var me = this;
Your example is really unclear, where does the error happen? You're calling data.setXml() with no parameters, so me.xml will bet set to undefined. That is to be expected if you pass nothing into the method.
Also keep in mind that due to the async nature of your call, if you were to do something like:
data.loadXml();
console.log("data.getXML();", data.getXML()); // would be undefined
data.getXML() at that moment would still be undefined as it's likely your asynchronous call hasn't returned yet, thus not setting the xml attribute of your object.
I'm trying to use a closure (I think that's what it is..), I'd just like to execute a function with a local variable at some point in the future, like this:
function boo() {
var message = 'hello!';
var grok = function() { alert(message); }
foo(grok);
}
function foo(myClosure) {
$.ajax({
timeout: 8000,
success: function(json) {
myClosure();
}
}
}
I could get around this by using global variables and such, but would rather use something like the above because it at least seems a bit cleaner. How (if possible) do you do this?
Thanks
----------- Update --------------------
Sorry I wasn't clear - I was wondering if this is the correct syntax for the closure, I tried it out and it seems ok. Thank you.
Your existing code looks perfectly fine except for that missing paren at the end. ;)
If you're looking to understand the concept of closures more deeply, think of it this way: whenever something in a closured language is defined, it maintains a reference to the local scope in which it was defined.
In the case of your code, the parameter to $.ajax() is a newly-created object ("{ timeout: 8000, etc. }"), which contains a newly-created function (the anonymous "success" function), which contains a reference to a local variable ("myClosure") in the same scope. When the "success" function finally runs, it will use that reference to the local scope to get at "myClosure", even if "foo()" ran a long time ago. The downside to this is that you can end up with a lot of unfreeable data tied up in closures -- the data won't be freed until all references to it have been removed.
In retrospect, I may have confused you more than helped you. Sorry if that's the case. :\
Unless you actually want to make an AJAX call, setTimeout might be more along the lines of what you are looking for:
function foo(myClosure) {
setTimeout(myClosure, 8000); // execute the supplied function after 8 seconds
}
If your question was more along the lines of "Am I creating a closure correctly?", then yes, your function boo is doing the right thing.
Is it what you want?
var boo = (function() {
var message = 'hello!';
return function() {
foo(function() {
alert(message);
});
};
})();
function foo(myClosure) {
$.ajax({
timeout: 8000,
success: function(json) {
myClosure();
}
}
}
or just
function boo() {
$.ajax({
timeout: 8000,
success: function(json) {
alert('hello!');
// do sth with json
// ...
}
}); // <- missed a paren
}
The example is too simple to know what you want btw.
I'm currently having an issue where I have a javascript object that is trying to use setInterval to call a private function inside of itself. However, it can't find the object when I try to call it. I have a feeling that it's because window.setInterval is trying to call into the object from outside but doesn't have a reference to the object. FWIW - I can't get it to work with the function being public either.
The basic requirement is that I may need to have multiple instances of this object to track multiple uploads that are occurring at once. If you have a better design than the current one or can get the current one working then I'm all ears.
The following code is meant to continuously ping a web service to get the status of my file upload:
var FileUploader = function(uploadKey) {
var intervalId;
var UpdateProgress = function() {
$.get('someWebService', {},
function(json) {
alert('success');
});
};
return {
BeginTrackProgress: function() {
intervalId = window.setInterval('UpdateProgress()', 1500);
},
EndTrackProgress: function() {
clearInterval(intervalId);
}
};
};
This is how it is being called:
var fileUploader = new FileUploader('myFileKey');
fileUploader.BeginTrackProgress();
Use this
intervalId = window.setInterval(UpdateProgress, 1500);
setInterval with a literal argument will eval this in the global scope where UpdateProgress is not accessible.
Because it is an eval expression, it does not have access to the scope that setInterval is created in. Try:
intervalId = window.setInterval(UpdateProgress, 1500)
It is generally good practice to avoid eval style expressions wherever possible. For instance, if you wanted to call several functions from the same timer, you would use an anonymous function instead of a string.
window.setInterval(function () {
function1();
function2();
}, 1500)
See also
Why is using javascript eval() a bad idea?
Anonymous function - Wikipedia
+1 to Andy E's head (I can't upvote yet, doh!)
Another gotcha that could get you is if you use this from within the called function.
Then doing exactly what Andy has with this addition should get you by.
var that = this;
window.setInterval(function() {
function1.apply(that);
function2.apply(that);
}, 1500);