I am trying to learn more about ajax calls and doing some testing. I am running into a snag due to async.. With the help of this website I have found what I believe should work.. Somehow things still arent running properly. I have fiddled a ton, but still have yet to make things work how Id like.
Background:
Capturing a variable amount of ids in array
Iterating through $.ajax calls to update ids (api batch request not possible).
$(document).ready(function() {
$("#button").click(function() {
var deffered, deferreds = [];
$.each(arr, function(i, array) {
deferred = $.ajax({ ...
success: function(data) {console.log('complete');},
error: function(data) {...}
});
deferreds.push(deferred);
});
$.when.apply($, deferreds).done(console.log('done'));
});
});
When running code above (also see fiddle for more details) and checking my console.log it shows 'done' occurring before any 'completes' are made (picture below).
Please help me understand what is wrong with code. I want all ajax calls to console.log('complete') before the console.log('done'). From my understanding that is the point of $.when.apply.
I have added the sample working code. Please add promise in each request
deferred = $.ajax({ ...
success: function(data) {console.log('complete');},
error: function(data) {...}
}).promise();
and change in this line
$.when.apply(this, deferreds)
Please run this code and verify it
function doSomething() {
var arrDef = [];
for (var i = 0; i < 2; i++) {
var deferred = $.ajax({
url:"https://jsonplaceholder.typicode.com/todos/1",
type: "GET",
contentType: "application/json;odata=verbose",
success: function(data) {
console.log('complete');
},
error: function(data) {
console.log('fail');
}
}).promise();
arrDef.push(deferred);
}
return arrDef;
}
var getters = doSomething();
// use apply to call $.when
$.when.apply(this, getters).then(function() {
console.log('Final complete');
// correctly writes out all responses
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Related
I'm trying to call json object in another JS file but there seems to be a timing issue. So I put a setTimeout below but the setTiemout runs twice, first with the object populated, then again with the object undefined and then undefined the passed to the second JS file. I also tried clearTimeout but then it didn't run at all. Then I tried a boolean but it still ran twice. I think the issue might be cause of the deferred, is there any way around this?
var json = {};
$('.submit').on('click', function (e) {
e.preventDefault();
var input = $('.url-input');
var def = $.Deferred();
$.ajax({
type: "GET",
url: $(input).val(),
data: input.serialize(), // serializes the form's elements.
success: function(data) {
var category;
$(data).find('a[href*="categories"]').filter(function(){
var data = $(this);
category = data.text().trim();
json.category = category;
});
def.resolve();
return def.promise;
}
}).then(function () {
$('.cust-viz.viz-2').html('<iframe class="bubble_chart" src="bubble_chart.html" height="500"></iframe>');
if (json.category) {
hideShowViz('show');
}
});
});
}
setTimeout(function () {
json = json;
}, 5000);
based on answers, I did
var json ={};
$('.submit').on('click', function (e) {
e.preventDefault();
var input = $('.url-input');
$.ajax({
type: "GET",
url: $(input).val(),
data: input.serialize(), // serializes the form's elements.
}).then(function (data) {
var category;
$(data).find('a[href*="categories"]').filter(function(){
var data = $(this);
category = data.text().trim();
json.category = category;
});
if (json.category) {
$('.cust-viz.viz-2').html('<iframe class="bubble_chart" src="bubble_chart.html" height="500"></iframe>');
hideShowViz('show');
}
});
});
}
Still returning empty.
There are multiple issues with the code.
Regarding timing, you are likely to have issues with this pattern ...
$.ajax({
...
success: fnA
})
.then(fnB);
... especially if fnB is dependent on something done by fnA.
In practice, it's never good to mix a success handler written as an $.ajax() option with one written as a .then() callback.
So refactor as follows :
$.ajax({
...
})
.then(fnA)
.then(fnB);
Now,
if fnA is asynchronous, it must return a promise to inhibit progress to fnB until that promise resolves.
if fnA is wholly synchronous, as appears to be the case, you can return a value or , more typically, you would merge fnA with fnB.
Note: A success handler written as an $.ajax() option does not possess the same power, and that's the reason why we don't mix success: fn with .then().
$.ajax({
...
})
.then(fnAB);
With those rules in mind, you should be able to better debug the other issues.
I have a serie of ajax request which are all waited to be resolved before continuing
(achieved with $.when().then()):
function myfunc(offset) {
// setTimeout(function(){
return $.ajax({
url:"https://www.URL.com",
crossDomain: true,
dataType: "jsonp",
success: function (response) {
// console.log(response);
data = data.concat(response);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
// handle errors
}
});
// },offset/10);-
}
$.when( // call all ajax requests
myfunc(0)
,myfunc(2500)
,myfunc(5000)
,myfunc(7500)
,myfunc(10000)
,myfunc(12500)
,myfunc(15000)
,myfunc(17500)
)
.then(function() { // when all the ajax requests are terminated
console.log(data);
});
I would like to delay them a little bit to make it "more likely" that they end in some order.
(Cf. commented setTimeout in the code above).
I don't want to execute the first ajax request and then the second and then the third. They should just start with ~250ms delays one from another.
But my attempt only results in the promise to be resolved immediately, without the ajax request being completed and thus an empty data.
Is there a way to set a timeout and don't loose the wait for the ajax resolution? Or should I structure my code differently?
Like Rory I'm a bit skeptical about the usefulness of doing it, but if you want to do it, you'd do it by returning your own promise that you resolve in the ajax callback:
function myfunc(offset) {
var d = $.Deferred(); // Create "Deferred" object
setTimeout(function(){
return $.ajax({
url:"https://www.URL.com",
crossDomain: true,
dataType: "jsonp",
success: function (response) {
// console.log(response);
data = data.concat(response);
d.resolve(); // Resolve it
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
// handle errors
d.reject(); // Reject it
}
});
},offset/10);
return d.promise(); // Return its promise
}
Based on the above code and what it looks like you are doing (joining a bunch of responses in a specific order)... you could try something like below instead:
urlArr = [
"http://jsonplaceholder.typicode.com/posts",
"http://jsonplaceholder.typicode.com/comments"
];
function joinResponses(arr) {
var promises = arr.map(function(url) {
return $.ajax(url).then(function(res) {
return JSON.stringify(res);
});
})
return $.when.apply($,promises).then(function() {
var joined = "";
for(var i = 0; i<arguments.length;i++) {
joined += arguments[i];
}
return joined;
});
}
joinResponses(urlArr).then(function(myJoinedResponses) {
document.write(myJoinedResponses);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Based on what you are asking for though it sounds rather strange and it seems like you could be doing something better.
Rory point is very good, but in case you want to do it anyway.. I'd do something like that.
var arrayFunc = [func1, func2, func3]
for (var i = 0; i < arrayFunc.length; i++) {
(function() {
var j = i;
setTimeout(() => {
arrayFunc[j]();
}, i * 2500);
})();
}
Working Fiddle
edit: setTimeout function
edit2: Fixed the closure error
I am relatively a newbie to jquery and ajax and am trying to use the concept of deferrals and promises to solve this problem I have.
I would like to do the following:
Call a list of URLS and process the result returned from the urls. I would like to first process the results in parallel, and then combine the processed results to give me a final result.
Th pseudo-code is as follows:
var deferredAjaxCalls = [];
for (var i = 0; i < jobsListLength; i++) {
deferredAjaxCalls.push(
$.ajax({
url:"/myurl",
method:"POST",
contentType:"application/json",
dataType:"json",
data:mydata,
success:function(result){
//Some code here that is performance intensive
}
});
}
$.when.apply(this,deferredAjaxCalls).done(function(){
for (var k=0; k< arguments.length;k++){
//combine the results of the individual results of the
// success part of all the ajax calls and execute some more
//code synchronously
}
}).fail( function (jqXHR, status, error) {
//Log failed status
});
Initially, I moved all of the code from the success part inside the $.when.apply().However, this resulted in very slow performance as there is a lot of intensive computation that is now executed synchronously. So I am looking for a way to execute part of the code independently, and the final piece synchronously
I did read about using promises, but could not find any example where promises are used with an array of ajax calls with intermediate processing before finally synchronising in the when.apply() block
What would be a good way to solve this problem?
Thanks!
Starting with an array jobsList, you probably want something like this :
var deferredAjaxCalls = jobsList.map(function(job) {
return $.ajax({
url: "/myurl",
method: "POST",
contentType: "application/json",
dataType: "json",
data: mydata
}).then(process);// where `process` is a function that accepts $.ajax's (data, textStatus, jqXHR) and returns a *single* value/object - the result of the processing. This will standardise the data delivered below by $.when() to its success handler.
});
$.when.apply(null, deferredAjaxCalls).then(function() {
// Due to `.then(process)` above, `arguments` are guaranteed to comprise one arg per ajax call.
// Otherwise you potentially have the problem reported here - http://stackoverflow.com/questions/12050160/
for (var k=0; k<arguments.length; k++) {
// Combine the results of the individual results of the success part of all the ajax calls and execute some more code synchronously.
}
// In this function deliver an error by returning `$.Deferred().reject(new Error('myReason'))`
return combined_result;
}, function(jqXHR, status, error) {
// This hander will receive multiple $.ajax() params, which are best normalised into a single Error object.
return new Error(status); // similar to .then(process) above, reduce $.ajax's error args to a single "reason".
}).then(null, function(err) {
// All errors delivered by code above arrive here as a js Error.
// But please note that, in jQuery <v3.0, any uncaught errors above will genuinely throw (to the console).
console.log(err.message);
});
You can try using deferreds:
var req_responses = [];
var deferreds = [];
for(var i in jobs) {
deferreds[i] = new $.Deferred();
}
for(var i in jobs) {
(function(i) {
$.ajax ({
url: ".",
type: "POST",
dataType: "json",
done: function(response) {
//process the response
req_responses[i] = response;
deferreds[i].resolve();
}
});
})(i);
}
$.when.apply(deferreds).then(function(os) {
//all the responses are in req_responses
//finish processing
alert("done");
});
I have a in an html page that I'd like to show a spinner on while a JQuery function is running. Since its asynchronous I am trying to use deferred to determine when the function completed
the code in the html page
var req = jslongfunc(value);
req.done(function( response){
spinner.stop();
});
The code in the JS page that contains jslongfunc
function jslongfunc() {
rdef = $.Deferred();
$.getJSON('mycode, function(data){
... do lots of stuff here ...
});
setTimeout(function () {
r.resolve();
}, 4500);
return r.promise();
The spinner seems to run for 4500 regardless of when the jslongfunc finished all its code. I know when it finishes because it draws something. I thought r would return and .done would execute if the function finished before the time out. What am I doing wrong?
Your promise is resolving only when your setTimeout function is calling r.resolve() after 4500ms. You need to call r.resolve() when all your stuff is done. Maybe something like this...
// function definition
var jslongfunc = function(defr) {
//... do lots of stuff here ...
defr.resolve();
}
// promise helper
var promise = function (fn) {
var dfd = $.Deferred(fn);
return dfd.promise();
}
// init the deferred by using the promise helper above and then attach 'done' callback to stop spinner
var rdef = promise(jslongfunc).done(function (response) {
spinner.stop();
}).fail(function (response) {
// handle promise failure.
});
Update
Well now that you put up your using $.getJSON you can do the following.
function jslongfunc(value) {
var jqxhr = $.getJSON(url, function (data) {
// THIS IS YOUR FIRST promise callback
// do some stuff with the json you just got
});
// return the promise
return jqxhr;
}
// now handle the spinner stoppage
jslongfunc(value).done(function () {
spinner.stop();
}).fail(function (data) {
// handle function failure here
});
Here is a little FIDDLE that I use to hit the Google financial API for stock quotes.
I've added the Ajax events beforesend, complete that turns some gears off and on (very short period of time for this call).
But it will probably give you a start.
Edit: Here's a second FIDDLE with more compact code.
JS
$('.putmehere1').hide();
var quotevalue;
var symboltopass = "NYSE:SDY";
$.ajax({
type: "GET",
url: "https://finance.google.com/finance/info",
async: false,
dataType: 'JSONP',
data: {
client: 'ig',
q: symboltopass
}
})
.done( function(data){
var stockInfo = data[0];
console.log( data[0] );
quotevalue = stockInfo.l;
$('.putmehere2').html( '#2 - ' + quotevalue);
});
$.ajax({
beforeSend: function(){
$('.putmehere1').show();
},
complete: function(){
$('.putmehere1').hide();
}
});
I have the following javascript code:
function initSite(){
var site;
$.getJSON(www+'init/initSite', function(data) { site = data; });
}
$(document).ready(function(){
var site = initSite();
console.log(site);
}
which returns undefined... how can i store the json object that i recieve in the site variable so i can use it later?
EDIT:
This seem to work but im not sure if its correct to use this solution
var site = null;
$.ajax({
url: www+"init/initSite",
async: false,
dataType: 'json',
success: function (data) {
site = data;
}
});
console.log(site);
of course you got undefined because your function doesn't return anything and the ajax call is also asynchronous, so you have to wait the server response. Since $.ajax (and shortcuts) returns a promise you can do this task using deferred
function initSite(){
return $.getJSON(www+'init/initSite');
}
$(document).ready(function(){
$.when(initSite()).done(function(data) {
/* continue here the code execution, e.g. call another function */
doAllTheRemainingWorkWith(data)
});
}
as you can see this code is short and easy to read
function initSite(onSuccess){
$.getJSON(www+'init/initSite', onSuccess);
}
$(document).ready(function(){
initSite(function(data){
var site = data;
// initialize your code.
});
}
The problem is just a miss concept:
getJSON is an async call, and the site = data; will only happen way after the DOM is ready.
in order for you to make everything work the way it should, your initialization needs to start from your async call result and never before, for example:
// no need to wait for DOM ready to call `initSite`
initSite();
function initSite() {
$.getJSON(www+'init/initSite', function(data) {
initialization(data);
});
}
function initialization(site) {
// initialize all the things that need to be done
console.log(site);
}
$(document).ready(function(){
// do other stuff, for example show a loading message/image
}