i set up a webservice thats cross domain and needs to be contacted via json with padding
on a simple jquery codeline like this, i am successfull getting back json data.
$.getJSON("http://server/series/hist?jsonp=?", function(data){
console.log(data);
});
the webservice, will wrap the result in a function, whenever "jsonp" exists within in the url.
for those cases i used a default function name like:
myfunction({"a":1})
jquery helps me out here, and trys to call the function, that isnt existing ("myfunction()"). what i am trying to achieve instead is a simple call of the callback function (see above), to handle the data locally.
can you point me in the right direction?
thank you
I'm not quite sure what your problem actually is, but:
Interpretation 1
Assuming that by "locally" you mean "without using a callback":
That is impossible. JSON-P cannot work synchronously as it depends on the addition of a <script> element (which won't be processed until the current function has finished executing).
Interpretation 2
Assuming that by that isnt existing ("myfunction()") you mean "Your webservice always uses the function name myfunction:
Fix the webservice. jsonp=? means "Randomly generate a function name and pass it as the jsonp parameter.
The webservice must use that parameter to determine the function name used, and not use a fixed value such as myfunction.
Interpretation 3
You don't want to use JSON-P as the input, but to call your anonymous function directly.
You can't. It isn't stored anywhere you can access it. You have to rewrite your code so it isn't passed directly to getJSON:
function myFunction(data){
console.log(data);
}
$.getJSON("http://server/series/hist?jsonp=?", myfunction);
myfunction({"a":1})
Related
Before anyone marks it as duplicate, this post does not actually answer the question but suggests a different way altogether to solve that particular issue.
Mine is a different issue. Please let me explain.
In my case, there are various .js files (plugins) which are being loaded with jquery getscript and stored in variables. Then whenever required they will be executed (more than once)
The code for loading script (this code will only run once at the init of the system for each plugin js file)
var storedFunc;
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
storedFunc = data;
});
All the plugins are in this format
(function(){
//lots of code here
})()
But when I checked the storedFunc variable in console, I found out that it has been stored as String variable. Like this,
"(function(){
//lots of code here
})()"
Now to execute this, I used eval, like this (this code can be executed multiple times based on the need)
eval(storedFunc)
Everything is working fine and i am happy with it, but here comes the problem, I read in somewhere that the usage of eval is kind of like a bad thing to do. So now I am afraid that thought everything is working fine, all these negativity of using eval spread on the internet might scare my client away. :(
So, please tell me how I can run that stored function (which has become a string) without using eval.
Or should I use anything else than $.getScript which does not convert a function into a string ?
Or if there is any other way altogether rewriting this plugin functionality?
Please show me the way. I am in need of this solution badly.
Any help will be appreciated.
Thanks in advance.
Understanding how $.getScript works
Seems there is some confusion on how $.getScript works. If you notice jQuery's documentation on the method, and as #Pointy made mention of in the comments, this is stated:
Load a JavaScript file from the server using a GET HTTP request, then execute it.
Here's an example: Let's pretend the contents of the file being returned is only this:
// Contents of yourExternalFile.js
console.log('Executed!');
Now, when you use $.getScript:
$.getScript(pathToPluginJSFile, function( data, textStatus, jqxhr ) {
// The script you retrieved has already executed at this point, and you will find "Executed!" in the console.
console.log('All Done');
});
Console output:
> Executed!
> All Done
The $.getScript method is not meant to be used to return a string of the content of the file. However, while that data is available in the callback, the contents of the file have already been executed. So by taking the string version of the file, and re-executing it with either new Function, or even eval, you are executing it twice on the page (jQuery does it once, and so do you).
Original Post:
Use the Function constructor instead of using eval.
// Your function as a string stored to a variable
var stringFunction = "(function(){console.log('Executed');})()";
// Use the Function constructor to create a new function:
var executableFunction = new Function(stringFunction);
// Now you can execute it
executableFunction(); // logs "Executed"
This snippet from this SO question/answer addresses the difference between eval and new Function.
eval() evaluates a string as a JavaScript expression within the current execution scope and can access local variables.
new Function() parses the JavaScript code stored in a string into a function object, which can then be called. It cannot access local variables because the code runs in a separate scope.
Additional Information (Based on comments)
Yes, you can just get the string contents of the file and store them to a variable without the contents of the file executing. You can have that function to execute anytime. You just need to use the regular get method using jquery, and set the dataType to text. This way, the script will not execute, and you can execute it as you see fit:
var storedFunction;
$.get({url: pathToPluginJSFile, dataType: 'text'})
.done(function (data) {
// Turn the script into a new function and store it
// The information in the script file has not done anything yet
storedFunction = new Function(data);
})
.fail(function () {
console.log('Failed :(');
});
The only thing you will have to watch out for, is making sure that the function was assigned to the storedFunction variable as you are making an api call, and you have to wait for that to finish before attempting to make the function execute.
// Later on, call that function anytime, and as often as you want:
storedFunction();
It appears that when I use my own callback function via JSONP, Flickr wraps the JSON with its own function name called jsonFlickrFeed. And if I type the url/src directly on the browser, I do see that it's indeed wrapped.
Does this mean I'm supposed to name my function to jsonFlickrFeed? I thought when using JSONP, we get to name our own functions?
P.S. I've browsed the site for answers, but I couldn't find any direct answer. The only answer I found was that someone did use jsonFlickrFeed as the callback function name; however, being forced to use the name is what I want clarification on.
I'm also using javascript without jquery.
If you read the Flickr documentation under the callback section it says if you want to specify the callback you need to set jsoncallback to the value you want. They do not follow the pattern of "callback" like most sites use.
Callback Function
If you just want the raw JSON, with no function wrapper, add the
parameter nojsoncallback with a value of 1 to your request.
To define your own callback function name, add the parameter
jsoncallback with your desired name as the value.
nojsoncallback=1 -> {...}
jsoncallback=wooYay -> wooYay({...});
http://api.flickr.com/services/feeds/photos_public.gne?tags=cat+pic+%22&tagmode=any&format=json&jsoncallback=xxx
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 am attempting to do some dynamic loading that includes javascript, css, and html files.
I would like to do it like this:
$.when($.ajax(htmlPath), $.get(cssPath), $.ajax({
url: javascriptPath,
dataType: "text"
}))
.done(function(response){
// i want to pass the data for each of these to the respective functions
appendHtml(what goes here??);
appendCss(what goes here??);
executeJs(what goes here??);
})
.fail(function(){
console.log("failed");
});
So I'm confused on how I separate out the response callbacks. Currently, the response object you see in my .done function is ONLY the HTML file which I called. This function is making the correct ajax calls, and the correct files are all being responded by the server, but how do I access them once ALL the calls are complete? Need this so I won't be applying css/js to HTML which is not there yet, etc. Also, what I do is have the javascript file returned as a string and then i eval() it within the executeJs function. My understanding is that this is an okay use of eval because its the file being returned by our own server so I don't see how it could be tampered with. Is this assumption correct?
Furthermore, in my appendCss function, I'm just adding it to a "style" element in the head. Is there a big issue with this? I am using all this to make a "widget/app based" functionality where I have a js,css,and html for each "app", and I just want to query the server for them when they are needed and the app is loading.
If your downloaded data is being retrieved from the same server as your original web page, then yes, generally, you would have the same level of trust in that code as you do in the code that's already running in the browser.
The problem with eval() in a context like this isn't necessarily that you don't trust the code coming back from your own server; it's that someone might be able to alter the running javascript so that the javascriptPath variable points somewhere you didn't expect it to.
As far as your actual question goes, your done callback will actually be passed three parameters, because your when call included three promises.
Because of the way that you defined your callback (as function(response)), you are only seeing the first one -- the return value from the HTML call. The other two parameters are being ignored.
Each of the three parameters that you are being passed will be an array of three elements: [wasSuccessful, statusText, jqxhr]. To do something useful with them, you could structure your callback something like this:
$.when($.ajax(htmlPath), $.get(cssPath), $.ajax({
url: javascriptPath,
dataType: "text"
}))
.done(function(htmlResponse, cssResponse, jsResponse){
if (htmlResponse[0]) {
appendHtml(htmlResponse[2].responseText);
}
if (cssResponse[0]) {
appendCss(cssResponse[2].responseText);
}
if (jsResponse[0]) {
executeJs(jsResponse[2].responseText);
}
})
(Assuming that you have the appropriate appendHtml, appendCss, and executeJs functions written already)
There are some good examples on this page: http://api.jquery.com/jQuery.when/
And this page has the documentation on the jqxhr object (the third element in each of the arrays that are passed to your done function): http://api.jquery.com/jQuery.ajax/#jqXHR
To access to all the responses just pass three arguments to the done() callback. Try this:
$.when($.ajax(htmlPath), $.get(cssPath), $.ajax({
url: javascriptPath,
dataType: "text"
}))
.done(function(responseHTML, responseCSS, responseJS){
console.log(responseHTML[0]);
console.log(responseCSS[0]);
console.log(responseJS[0]);
})
if you try to print arguments object inside done() you can clearly see that all the responses are passed into the callback
Regarding the use of eval, consider using JSONP instead (dataType: 'jsonp'). This way jQuery takes care of executing the code for you. I suppose jQuery also uses eval() under the hood, but then at least you know that it is done in a proper manner. With respect to safety, also see the related question on when eval() is evil if you haven't already.
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.