I've written a function which makes an asynchronous request using jQuery.
var Site = {
asyncRequest : function(url, containerId) {
$.ajax({
url : url,
onSuccess: function(data){
$(containerId).html(data);
}
});
}
}
Syntax might be slightly wrong as I'm using notepad, but hopefully you get the idea.
I call the function:
Site.asyncRequest('someurl', container1);
Site.asyncRequest('someurl', container2);
Both requests get sent and processed by the server. Two responses get sent back, which is what I expect. However, I would expect container1 and container2 to contain responses from both requests.
The problem, is that only the last response gets displayed and I can't figure out why. I don't know how the jQuery ajax keeps a track of requests/responses, so maybe this is a problem.
Say I make 5 or 10 requests, how does jQuery ajax know which response is for which request and where does it keep a track of it?
Thank you
This appears to be a Javascript scoping issue. Try the following:
var Site = {
asyncRequest: function(url, containerId) {
(function(theContainer) {
$.ajax({
url: url,
onSuccess: function(data) {
$(theContainer).html(data);
}
});
})(containerId);
}
};
This creates a separate scope for each function call, so the actual value pointed to by "theContainer" is different for each onSuccess anonymous function.
What is happening here is a single closure is getting created, due to the way that function is declared. See "A more advanced example" here: http://skilldrick.co.uk/2010/11/a-brief-introduction-to-closures/
Basically, the containerId is being shared among all instances of that onSuccess anonymous function. I haven't tested this, but I believe if you defined your asyncRequest function outside of Site, this would work.
As far as a more elegant solution to this problem, perhaps someone else will answer better.
Related
The code is very complex so i have simplified below in order to confirm if the behavior i am experiencing is normal or due so some other error i have made in the code.
I have two separate ajax requests that each have their own unique call back. I do not care which one completes first and one has no dependency on the other
function ajax(url, cbS){
$.ajax({
url: url,
contentType: 'application/json',
dataType: 'json',
success: function(data){
cbS(data)
},
});
}
function callbackSuccess1(data){
$('#div1').html(data)
}
function callbackSuccess2(data){
$('#div2').html(data)
}
//request#1
ajax(myapiurl+'&peram1=100', callbackSuccess1);
//request#2
ajax(myapiurl+'&peram2=200', callbackSuccess2);
The problem: Sometimes callbackSuccess1 gets the data intended for request#2 and vice versa.
It seems that which ever request completes first fires callbackSuccess1 and the second to complete fires callbackSuccess2.
I need the callback to be bound to it's specific request so that regardless of the order in which they complete each request fires it's proper callback.
OTHER INFO: My backed is django-tastypie, at this point i am thinking that tastypie is somehow messing up the response. That is the only logical conclusion, given that the javascript seems to be immutable.
The proof that this is actually occurring is that when i inspect the responce on request#1 the data objects are clearly intended for request#2...
CONCLUSION:
Thanks for confirming that 'each invocation of your ajax() function will create it's own closure'. This was what i thought was going wrong. I found the problem in my API. I was doing some funky stuff and it looks like I had a variable that was not getting trashed in time causing the API to return the wrong data if the first request took longer than the second.
The only issue I see with the code you have included is that the function argument is cbS, but you are calling cbs(data) - note the different capitalization.
Other than that, each invocation of your ajax() function will create it's own closure and have it's own arguments and those arguments will be preserved separately for the internal success callback. This is an important capability in javascript and it works. It does not get the arguments of one call confused with the callback of another as long as you are not using any global variables or state that might change during the execution of the asynchronous ajax call.
You could probably use jsonp and specify callback query parameter in URL for $.ajax
callback would be the name of javascript function which is to be invoked whenever the response is returned from server.
For more details please refer jquery doc : http://api.jquery.com/jQuery.ajax/
For theory : http://en.wikipedia.org/wiki/JSONP
This is mainly used for cross-site ajax calls.
I feel stupid because I've been trying to access this response variable for a while and I guess I do not understand closures or scope well enough, so please help.
I'm working on a chrome extension and I am sending a message from contentscript.js to background.js and receiving the response. Now I want to return the response and be able to use it in contentscript.js. Seems like something you should be able to do...
function getWords(){
var words = [];
chrome.runtime.sendMessage({detail: "words"}, function(response) {
console.log(response) // prints ["word1, "word2" ..]
words = response;
});
return words; // = []
}
UPDATE:
Thanks, I understand what my issue is now, but still would like some advice to solve it.
My question is what is the best way to "ask" the background page for a list of words if I need it immediately as a parameter in another function. Can I wait for the information to come back? Should I simply call that other function from the callback? or is there some other method?
Ideally I would like to actually implement a getWords() that doesn't return until the list comes back... Impossible? I'm open to open source libraries as well.
Because sendMessage is an asynchronous call and you are treating it as a synchronous one. You are trying to read words before the call is actually made. There is no way to wait for it. You need to use callbacks.
function getWords( callback ){
var words = [];
chrome.runtime.sendMessage({detail: "words"}, function(response) {
console.log(response) // prints ["word1, "word2" ..]
callback(response);
});
}
function processWords(words){
//do your logic in here
console.log(words);
}
getWords(processWords);
I'm sure the solution is staring me right in the eyes, but I just cannot see it. I am trying to load an object from an outside file source. I've tried it several which ways using jQuery's built in methods, but keep returning undefined. Is my issue the scope? I need partnerData right where it is because of other dependent methods in my script. I don't want to operate the rest of my site's functions from within the $.get callback. Any help is greatly appreciated, here's the code:
$(function() {
var partnerData;
$.get('data/partners.json', function(file) {
partnerData = $.parseJSON(file);
});
console.log(partnerData); /* returns undefined instead of object */
});
EDIT:
Thanks for all the feedback everyone. This is the solution I went with:
var partnerData;
$.ajax({
url: 'data/partners.json',
dataType: 'json',
async: false,
success: function(data) {
partnerData = data;
}
});
The reason why you're seeing undefined is because ajax requests are asynchronous by default. This means your get method gets invoked and the code flow moves down to the next statement while the request executes in the background. Your callback function is later invoked when the request completes.
Using callback functions is a common pattern used in situations like this. But you seem to be saying you don't want to do or can't do that. In that case, you could use async: false which would force the request to be synchronous. Keep in mind however, that your code will be blocked on the request and if it's a long-lived request, the user experience will degrade as the browser will lock up.
P.S. You shouldn't need to parseJSON - if response has the correct mime-type set, jQuery will intelligently guess the type and parse the JSON automatically. And in case the server isn't sending back the correct mime-type, you can also explicitly tell jQuery what the expected return data type is; see the dataType argument to $.get() .
One way you might modify your code, to force synchronous requests:
$.ajax({
type: 'GET',
url: 'data/partners.json',
success: function(file){
partnerData = $.parseJSON(file);
//ideally you would perform a callback here
//and keep your requests asynchronous
},
dataType: 'json',
async: false
});
function is proccessed to the end event when ajax is still being proccessed. insert it into callback function
$(function() {
var partnerData;
$.get('data/partners.json', function(file) {
partnerData = $.parseJSON(file);
console.log(partnerData);
});
});
I would say that your problem is the same of the one that I just solved, if $.get is AJAX! and it is setting a variable, to read that variable outside the callback you need to wait the response! So you have to set async=false!
console.log in synchronous and get is async.
try:
$(function() {
var partnerData;
$.get('data/partners.json', function(file) {
partnerData = $.parseJSON(file);
test();
});
function test(){
console.log(partnerData);
}
});
Can somebody explain to me the ascending order of the alerts, and the item values of the model in the following piece of backbone.js code?
var model = new Ingredient({"item" : "Before",});
alert("1");
alert(model.get('item')); // Before
model.fetch({ success: function() {
alert("3");
alert(model.get('item')); // After
}});
alert("2");
alert(model.get('item')); // Before
I can't seem to figure out how to update the state of the model in the same scope which it was defined. Is that important?
It's possible I'm thinking about this the wrong way, or I don't understand something fundamental about javascript scoping or functions.
Thanks
The success: function() is called asynchronously as it is really just a wrapper around a JQuery AJAX call. In human speak -. The fetch method makes a request to the server for the model data. The fetch method returns immediately and doesn't wait for the http request to complete. When the http request completes ( if successfully ) then the success: function()
callback is called. This will be the last thing that happens.
The jQuery documentation lists the following example of using $.getJSON to request JSONP:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",
function(data) {
$.each(data.items, function(i,item) {
$("<img/>").attr("src", item.media.m).appendTo("#images");
if (i == 3) return false;
});
});
Rather than use this method, which generates a dynamic callback function name because of this parameter:
jsoncallback=?
I want to be able to set that in advance to a hardcoded function name, like this:
jsoncallback=test
This works, in the sense that I run the script and the JSONP that I get back has the JSON object wrapped in a call to test().
However, I can't figure out how to set up the callback function. Shouldn't it be as simple as this?
function test(data) {
console.log(data);
}
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=test");
When I try that, I get back the JSONP which is wrapped in test(), but the function test() that I've defined is never called. Am I missing something?
Thanks for any help!
As defined in the documentation for you to use the following method
jQuery.getJSON(...)
you need to specify callback=? when making a JSONP call. I usually only uses this for response types of "json". For response types of "jsonp", you want to use:
jQuery.get(...)
and specify the type as "jsonp". See this documentation on the subject. But that is also bound by the fact of having to have a callback=?.
What I think you are looking for is this:
jQuery.getScript(...)
Which should execute whatever method you have defined in your callback.
Ah, the "Related" sidebar section saved me here. After I submitted this question, I found a similar one already asked:
using a named function as the callback for $.getJSON in jQuery to satisfy Facebook request signing demands
Duncan's answer from Oct. 15 solved this for me:
window.fixed_callback = function(data){
alert(data.title);
};
$(function() {
$.getScript("http://api.flickr.com/services/feeds/photos_public.gne?tags=cats&tagmode=any&format=json&jsoncallback=fixed_callback", function(data) {
alert('done'); } );
});
I guess the key is using $.getScript instead of $.getJSON. One can still specify an anonymous callback function in the parameters of the $.getScript method, which will be executed after the callback function named in the request URL parameters ("fixed_callback" in this case). Hope this helps someone down the road.