I have an application that makes multiple jsonp requests, all with the same jsonpCallback, which I can't change.
I tried to reproduce the situation in the following code.
The output is somewhat random, but it hardly includes all possible values for contextvar.
It seems to me that jquery registers "myFunc" globally, and that with each new request from requestMaker this association is overwritten.
How not to overwrite the contexts, in this case, how to print all the values?
<script type="text/javascript">
var requestMaker = function(url, contextvar) {
$.ajax({
url: url,
crossDomain: true,
dataType: 'jsonp',
jsonpCallback: "myFunc"
}).done(function(data) {
console.log(contextvar);
});
};
requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request1");
requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request2");
requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request3");
requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request4")
</script>
Yes, it looks like the callback function "myFunc" is occupying the global scope and can therefore not be run in parallel. The following snippet runs the four jsonp calls consecutively, one after the other. Not ideal, but at least you get your results. Some APIs offer a parameter callback or jsonp for specifying the name of the padding callback function. The API used here however does not offer such a parameter.
var requestMaker = async function(url, contextvar) {
return $.ajax({
url: url,
crossDomain: true,
dataType: 'jsonp',
jsonpCallback: "myFunc",
}).done(function(data) {
console.log(contextvar,data);
});
};
(async function(){
await requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request1");
await requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request2");
await requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request3");
requestMaker("https://www.w3schools.com/js/demo_jsonp.php", "request4");
})(); // execute the async IIFE
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
jsonp is considered to be a "historical" protocol for AJAX requests, initially proposed in 2004, see here: https://en.m.wikipedia.org/wiki/JSONP. It has certain security issues and was made obsolete by CORS in 2014.
Related
To successfully use JSONP (e.g. via jquery - $.ajax ... etc.) must always be that the requested page is designed to provide data corresponding to this format?
In other words, if I perform a request to a page with a pure static content (i.e. no php, aspx, and so on), also will I get an error?
This question might seem trivial to some users, but I'm starting right now to learn these technologies, and the matter is a bit complicated.
Based on these (ref1 ref2) references it would seem that there must be consistency between the request with JSONP and implementation of the server response.
Edit
I have this jQuery request
$.ajax({
url: "https://sites.google.com/site/problemsstore/javascript/test.js",
type: 'GET',
crossDomain: true,
dataType: 'jsonp',
dataCharset: 'jsonp',
success: function (result) {
console.log('request succeed');
},
error: function (result) {
console.log('failed');
}
});
And I have loaded in https://sites.google.com/site/mysite/javascript/test.js?attredirects=0&d=1 this test.js file:
function myCall(data) {
console.log('succeed');
}
myCall({ some : "data" });
When I am connected I hope to obtain as console's output: succeed succeed.
Instead this is what I get:
succeed
failed
Edit2
$.ajax({
url: "https://sites.google.com/site/bentofelicianolopez/jscript-jsonp/test.js?attredirects=0&d=1",
type: 'GET',
crossDomain: true,
dataType: 'jsonp',
dataCharset: 'jsonp',
jsonp: 'myCall',
//contentType: 'application/json',
success: function (result) {
console.log('request succeed');
},
error: function (result) {
console.log('failed');
}
});
The .js file:
myCall({ some : "data" });
The output:
failed test4.html:94:9
ReferenceError: myCall is not defined /*this is the syntactical error of which I said*/
test.js:1:1
To successfully use JSONP (e.g. via jquery - $ .ajax ... etc.) must always be, that the requested page is designed to provide data corresponding to this format?
Yes. A request for JSONP will only work if the response is expressed as JSONP.
In other words, if I perform a request to a page with a pure static content (i.e. no php, aspx, and so on), also I will get an error?
You can have a static JavaScript program that conforms to the JSONP format (it requires hardcoding the callback function name), so not necessarily.
I'm currently trying to resolve some with an SPA being built with a lot of custom components, borrowing a lot from all over the place.
The current challenge I have is initializing some settings (Endpoint URLs and the sort) with a settings file to be set on a per-deployment basis. It's Javascript, so it makes sense to relegate this to a JSON file. However, a lot of the logic is still written in this strongly-defined OO methodology, and I am trying to determine the best method to load this settings file within the async methodology.
Looking at other topics on Stack Overflow, the jQuery getJSON method is a viable option. But I am not clear if it is still based on synchronous calls. Furthermore, If there is an async option, I want to be sure that the value is loaded before firing any additional logic in the application (instead of having it initialize nothing at the constructor).
Is my assumption correct? Is getJSON the best approach here? Code is listed below.
var settings = $.getJSON("conf.json");
var SearchObject = new Search(settings);
// Remainder is just a bunch of bind() and on() calls
$.getJSON is just a shortcut for more detailed $.ajax call where you can adjust much more parameters, like callback if you need. And no, $.getJSON same as $.ajax
Perform an asynchronous HTTP (Ajax) request.
http://api.jquery.com/jQuery.ajax/
The getJSON jquery function is async by default.
$.getJSON({
url: url,
data: data,
success: function(data, textStatus, jqXHR ){}
});
It is the same as:
$.ajax({
dataType: "json",
url: url,
data: data,
success: function(data, textStatus, jqXHR ){}
});
But, with the ajax function you can set async to false if you want.
eg.
$.ajax({
dataType: "json",
async: false,
url: url,
data: data,
success: function(data, textStatus, jqXHR ){}
});
I have a dynamic mock setup using mockjax, and it works for most of my ajax requests, but fails when the dataType is set to Script, and lets the request fall through to regular Ajax handler.
// gets mocked
$.ajax({
type: "GET",
url: "http://myurl.com/myfile.js?_=1395314460347"
})
// does not get mocked!
$.ajax({
type: "GET",
dataType: "script",
url: "http://myurl.com/myfile.js?_=1395314460347"
})
How can I configure dynamic mocks in mockjax to intercept requests with the dataType set?
UPDATE: example code for mockjax definition
I am creating dynamic mock, so I am defining via function, not plain object, something like this...
$.mockjax(function(settings) {
// settings.url == '/restful/<service>'
var service = settings.url.match(/\/restful\/(.*)$/);
if ( service ) {
return {
proxy: '/mocks/' + service[1] + '.json',
// handle `dataType: 'script'`
dataType: 'application/javascript'
};
}
return;
});
This appears to be a bug with how Mockjax handles crossDomain script requests. It is not doing anything special to detect the crossDomain request (like it does with JSONP) and as such, when it passes the request back to the original $.ajax method – jQuery never uses the mocked up XHR object it was provided by Mockjax.
So in essence, Mockjax is intercepting the request, and then passes it right back to jQuery and it fails on you.
I opened an issue here so this can be fixed: https://github.com/appendto/jquery-mockjax/issues/136
In the mean time you have a two choices. If you want to quickly patch mockjax, add this line to around 471:
origSettings.crossDomain = false;
That section will look like this when you are done:
mockHandler.cache = requestSettings.cache;
mockHandler.timeout = requestSettings.timeout;
mockHandler.global = requestSettings.global;
origSettings.crossDomain = false;
copyUrlParameters(mockHandler, origSettings);
The other alternative (which I recommend against), is adding crossDomain: false to your actual AJAX request. I don't recommend this due to the need to remove that line when you remove your mocks later.
Thanks #Nicholas Cloud for pinging me and bringing this issue to my attention.
Are you setting the dataType property in your mocked endpoints?
See: https://github.com/appendto/jquery-mockjax#data-types
If you are, have you tried setting the mock dataType to application/javascript?
$.mockjax({
type: "GET",
dataType: "application/javascript",
url: "myfile.js?_=1395314460347",
responseText: "(function () { alert('hello world!'); }());"
});
$.ajax({
type: "GET",
dataType: "script",
url: "myfile.js?_=1395314460347"
});
I am using Springer OpenAccess API with JS, they provide their data with many ways, one of them is jsonp format. My code is shown below. What ever i do, i couldn't make it run synchronuously. Callback mechanism is good solution but i want to learn how to run this function syncronously or how enable this function to behave syncronous.
Any help will be appreciated.
SpringerAPI.prototype.getArticleInfo = function(doi){
//create url of article according to given doi...
var url = this.endpoint.host+this.endpoint.method+''+'?q=doi:'+doi+'&api_key='+this.endpoint.apikey+"&callback=?";
//get information about article...
//perform async request to the Springer API
this.situation = true;
var article;
jQuery.ajax({
method:'POST',
url: url,
dataType: 'JSON',
cache: true,
async: false, // to set local variable
success: function(data)
{
article = FromSpringerToArticle(data,-1);
}
});
return article;
};
You can't do synchronous JSONP because jQuery doc says
async
...
Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation...
You'll have to do an asyc operation and use callbacks.
UPDATE Following #Ryan Olds suggestion to include the setTimeout in the callback, I must clarify that in my production code I'm calling multiple urls to get json data from several sites. (Have updated JavaScript code below).
Is it only possible to have multiple timeouts scattered throughout this function?
I have a self-invoking updateFunction as follows:
(function update() {
$.ajax({
type: 'GET',
url: "http://myexample.com/jsondata",
dataType: 'json',
success: function (data) {
// do some callback stuff
},
async: false
});
$.ajax({
type: 'GET',
url: "http://myexample2.com/jsondata2",
dataType: 'json',
success: function (data) {
// do some further callback stuff
},
async: false
});
setTimeout(update, 2000);
})();
What I expected this code to do
I hoped that this function would go off to the target URL and wait for the result, then deal with the success callback. Then (and only then) would it fall through to set a 2 second timeout to call the function again.
What appears to be happening instead
Instead, the GET request codes out, and before the response has been dealt with, the timeout has already been set.
What am I missing? How can I make this entirely synchronous?
If I were you, I'd make use of jQuery's support for deferred action.
(function update() {
$.when($.ajax({
type: 'GET',
url: "http://myexample.com/jsondata",
dataType: 'json',
success: function (data) {
// do some callback stuff
}
}), $.ajax({
type: 'GET',
url: "http://myexample2.com/jsondata2",
dataType: 'json',
success: function (data) {
// do some further callback stuff
}
}), $.ajax({
// more requests as you like
})).then(function() {
// when all the requests are complete
setTimeout(update, 2000);
});
}());
Much nicer, IMHO, than mucking around with synchronous requests. Indeed, if the requests are cross-domain, this is pretty much your only option.
See
$.when
deferred.then
Move the timeout in to the success callback. The request is synchronous, it would appear the the callback is not.
I would modify the setup like so:
function update() {
$.ajax({
type: 'GET',
url: "http://myexample.com/jsondata",
dataType: 'json',
success: function (data) {
// do some callback stuff
},
async: false
});
$.ajax({
type: 'GET',
url: "http://myexample2.com/jsondata2",
dataType: 'json',
success: function (data) {
// do some further callback stuff
},
async: false
});
}
setInterval(update, 2000);
update(); // only necessary if you can't wait 2 seconds before 1st load.
You cannot make it entirely synchronous because you're setting up calls to alternate domains. That's done (internal to jQuery) by creating <script> tags and adding them to the document. Browsers perform those calls asynchronously, and that's that. You can't make ordinary xhr requests to domains different from your own.
I can't imagine why you'd want something like that to be synchronous, especially since you're doing many of these operations.
I don't think async: false works on cross domain requests.
From the docs:
async Boolean
Default: true
By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active.
In any case, maybe you can set some conditionals to fire the requests in the order that you want.