How to callback each loop after completion of ajax call.
Please find my code as follows.
Story:
Let us assume I have 3 values X,Y,Z. Firstly, I am taking X value, sending to django views and their using requests module for getting some info and pushing to the div class push_new_info_here, in the next Iteration I have to take Y value. How to do ? please note: Previous ajax call should be succeeded .
Final word: I am collecting all the info of (X,Y,Z), then merging using python and pushing to the div class push_new_info_here
window.onload = function() {
$.each(["X","Y","Z"], function( index, value ) {
$.post('/find/'+value+'/',{},function(data){
$('.push_new_info_here').empty();
$('.push_new_info_here').html(data);
});
});
};
Like this - use .append if you want to see the data or change the timeout to something long enough for the user to see:
var arr = ["X","Y","Z"],cnt=0;
function postIt() {
if (cnt >= arr.length) return; // stop
$.post('/find/'+arr[cnt]+'/',{},function(data){
$('.push_new_info_here').empty().html(data); // or .append
cnt++;
postIt(); // or setTimeout(postIt,3000); // give server a breather
});
}
$(function() {
postIt();
});
try wrapping the call in a function, then recursing through the array using the AJAX .done method. For example
window.onload = function() {
recursiveAjax(["X","Y","Z"])
};
function recursiveAjax(values){
//basic error testing
if (typeof values == "undefined" || values.length == 0) return false
var value = values.pop();
$.post('/find/'+value+'/',{},function(data){
$('.push_new_info_here').empty();
$('.push_new_info_here').html(data);
recursiveAjax(values)
});
}'
EDIT:
To avoid destroying the array we can send a cloned copy through to the function. For example:
window.onload = function() {
var tempArray = ["X","Y","Z"]
recursiveAjax(tempArray.slice())
};
Related
newby newb here...I don't think this specific issue has been addressed before on this site, I've searched and searched but can't find anything that works. I want to display a loading image. I am hiding it before my setTimeout and then showing right at the end of the setTimeout. Everything I've read said this should work but it doesn't. My image appears and then disappears right away before my script is finished running. Any ideas? Thanks!
function createHTML(data, listName) {
var arr = data.d.results;
$(".save").on("click",function(event){
// HERE IS WHERE I SHOW MY LOADING IMAGE INITIALLY
$('.logoImg').show();
// SET TIMEOUT FUNCTION
setTimeout(function() {
$("input.title").each(function(){
var title = $(this).val() ? $(this).val() : null;
var currentUserRequest = GetCurrentUser();
(function (userData) {
updateListItem(_spPageContextInfo.webAbsoluteUrl,'MyList',id,itemProperties,printInfo,logError);
function printInfo() {
console.log('Item has been UPDATED!');
}
function logError(error){
console.log(JSON.stringify(error));
}
});
});
// THIS IS FIRING BEFORE THE ABOVE EACH STATEMENT
$('.logoImg').hide();
}, 0);
});
}
The callback function passed to each will be called for each element that matches the selector.
It looks like you want to call hide() only after the callback has been called for all the elements.
To accomplish this, you need to call it after the last callback.
Something like this should works:
var titles = $("input.title");
var length = title.length;
titles.each(function(index, element) {
// Do what you need
if(index == (length-1)) {
// We are now on the last one
$('.logoImg').hide();
}
});
I am looking for a way to do a callback after two ajax calls completes:
$.when(
call1(),
call2()
).always(function() {
// Here I want to be sure the two calls are done and to get their responses
);
The catch is that one of the calls might fail. So, in my code the always will invoked without waiting to the other call.
How can I wait for both calls to done (success or failure)?
Here is something that should do the trick:
$.whenAllDone = function() {
var deferreds = [];
var result = $.Deferred();
$.each(arguments, function(i, current) {
var currentDeferred = $.Deferred();
current.then(function() {
currentDeferred.resolve(false, arguments);
}, function() {
currentDeferred.resolve(true, arguments);
});
deferreds.push(currentDeferred);
});
$.when.apply($, deferreds).then(function() {
var failures = [];
var successes = [];
$.each(arguments, function(i, args) {
// If we resolved with `true` as the first parameter
// we have a failure, a success otherwise
var target = args[0] ? failures : successes;
var data = args[1];
// Push either all arguments or the only one
target.push(data.length === 1 ? data[0] : args);
});
if(failures.length) {
return result.reject.apply(result, failures);
}
return result.resolve.apply(result, successes);
});
return result;
}
Check out this Fiddle to see how it works.
Basically it waits for all Deferreds to finish no matter if they fail or not and collects all the results. If we have failures, the returned Deferred will fail with a list of all failures and resolve with all successes otherwise.
It isn't pretty, but you could have a global "completed" variable for each ajax call to set when complete. Each call would also check whether both variables were set, and if so, call your always function.
You can also nest the calls:
$.when(call1()).always(function(){
$.when(call2()).always(function(){
// Here I want to be sure the two calls are done and to get their responses
});
});
But of course the two calls will become synchronous to each other.
Daff's answer is good. There is only one problem. When there is only one deferred, things don't work.
The problem was inside jquery's when method.
jquery.when: function( subordinate /* , ..., subordinateN */ ) { ...
It has a line like:
// If resolveValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
And this changes the shape of the arguments, so I had to put it back to the common shape my code expects (i.e. the same shape when multiple deferreds are passed to whenAllDone)
const jqueryWhenUsesSubordinate = deferreds.length == 1;
const deferredArgs = jqueryWhenUsesSubordinate
? [[ arguments[ 0 ], arguments[ 1 ] ]]
: arguments
$.each(deferredArgs, function (i, resolvedArgs) {
var target = !resolvedArgs[0] ? failures : successes;
var data = resolvedArgs[1];
target.push(data.length === 1 ? data[0] : data);
});
Additionally, I changed the function signature to match more closely to Promise.allSettled in that it should take an array parameter of deferred objects, then instead of looping over arguments to set up the deferreds array, you loop over that parameter passed in.
This allows you to programmatically create a variable length of deferreds into an array and pass that into whenAllDone.
I have a javascript array. I need to process each element of this array and then store the entire array into an object store. The kind of processing I do on the elements should be async. as shown in the following method.
var x = [ele1,ele2,ele3] ;
x.forEach(function(ele,index){
if(ele == "some specific object"){
MyAPI.process("command",function(result){
x[index] = result;
});
database.store(x);
The problem when calculating asynchronously is that the elements aren't processed in order. The key would be to keep track of the number of results that have been processed then, and only then, call the store method. The most effective way is to move the call to the store method into the callback. That way you can call it directly after the check for the last element succeeds rather than having to do a busy/wait while checking the flag condition. Another possibility would be to raise an event when the last element is processed and have the store take place in the event handler (or something similar).
var x = [ele1,ele2,ele3] ;
var expectedResults = 0;
var results = 0;
x.forEach(x,function(ele,index) {
if (ele == "some specific object") {
++expectedResults;
}
});
x.forEach(x,function(ele,index){
if (ele == "some specific object"){
MyAPI.process("command",function(result){
x[index] = result;
if (++results == expectedResults) {
database.store(x);
}
});
}
});
//I have the following function:
function handle_message(msg)
{
//do work
console.log('some work: '+msg.val);
//call next message
msg.next();
}
//And array of message objects:
var msgs = [ {val : 'first msg'}, { val : 'second msg'}, { val : 'third msg'}];
//I link messages by setting next parameter in a way that it calls handle_message for the next msg in the list. Last one displays alert message.
msgs[2].next = function() {alert('done!')};
msgs[1].next = function() {handle_message(msgs[2]);};
msgs[0].next = function() {handle_message(msgs[1]);};
//Start the message handle "chain". It works!
handle_message(msgs[0]);
//======== Now I do exactly the same thing but I link messages using the for loop:
for (var i=msgs.length-1; i>=0; i--)
{
if (i==msgs.length-1)
{
msgs[i].next = function() {alert('done!');};
}
else
{
msgs[i].next = function() {handle_message(msgs[i+1]);};
}
}
//Start the message handling chain. It fails! It goes into infinite recursion (second message calls itself)
handle_message(msgs[0]);
Can sombody explain why it happens? Or maybe an alternative to this pattern? My case is this: I receive an array with messages and I have to handle them in order, one ofter another SYNCHRONOUSLY. The problem is some of the messages require firing a series of animations (jqwuery animate() which is async) and the following messages cannot be handled until the last animation is finished. Since there is no sleep() in javascript I was trying to use such pattern where the message calls the next one after it is finished (in case of animations I simply pass the 'next' function pointer to animate's "complete" callback). Anyway, I wanted to build this 'chain' dynamically but discovered this strange (?) behaviour.
You need a closure to make it work:
function handle_message( msg ) {
console.log( 'some work: ' + msg.val );
msg.next();
}
var msgs = [{val :'first msg'},{val:'second msg'},{val:'third msg'}];
for ( var i = msgs.length - 1; i >= 0; i-- ) {
(function(i) {
if ( i == msgs.length - 1 ) {
msgs[i].next = function() { alert( 'done!' ); };
} else {
msgs[i].next = function() { handle_message( msgs[i + 1] ); };
}
})(i);
}
handle_message( msgs[0] );
Live demo: http://jsfiddle.net/simevidas/3CDdn/
Explanation:
The problem is with this function expression:
function() { handle_message( msgs[i + 1] ); }
This function has a live reference to the i variable. When this function is called, the for loop has long ended and the value of i is -1. If you want to capture the current value of i (the value during the iteration), you need to an additional wrapper function. This function captures the current value of i permanently (as an argument).
I think the problem is that i doesn't have the value you think it has:
// i is defined here:
for (var i=msgs.length-1; i>=0; i--)
{
if (i==msgs.length-1)
{
msgs[i].next = function() {alert('done!');};
}
else
{
msgs[i].next = function() {
// when this line gets executed, the outer loop is long finished
// thus i equals -1
handle_message(msgs[i+1]);
};
}
}
See point #5 Closures in loops at http://blog.tuenti.com/dev/top-13-javascript-mistakes/
Think about the values you are capturing in the closure.
msgs[i].next = function() {handle_message(msgs[i+1]);};
This captures the value of i, but it changes the next iteration so you get an infinite loop.
By the end of the loop i is -1 so i+1 is going just going to be the same message over and over again.
I think this issue goes beyond typical variable scope and closure stuff, or maybe I'm an idiot. Here goes anyway...
I'm creating a bunch of objects on the fly in a jQuery plugin. The object look something like this
function WedgePath(canvas){
this.targetCanvas = canvas;
this.label;
this.logLabel = function(){ console.log(this.label) }
}
the jQuery plugin looks something like this
(function($) {
$.fn.myPlugin = function() {
return $(this).each(function() {
// Create Wedge Objects
for(var i = 1; i <= 30; i++){
var newWedge = new WedgePath(canvas);
newWedge.label = "my_wedge_"+i;
globalFunction(i, newWedge]);
}
});
}
})(jQuery);
So... the plugin creates a bunch of wedgeObjects, then calls 'globalFunction' for each one, passing in the latest WedgePath instance. Global function looks like this.
function globalFunction(indicator_id, pWedge){
var targetWedge = pWedge;
targetWedge.logLabel();
}
What happens next is that the console logs each wedges label correctly. However, I need a bit more complexity inside globalFunction. So it actually looks like this...
function globalFunction(indicator_id, pWedge){
var targetWedge = pWedge;
someSql = "SELECT * FROM myTable WHERE id = ?";
dbInterface.executeSql(someSql, [indicator_id], function(transaction, result){
targetWedge.logLabel();
})
}
There's a lot going on here so i'll explain. I'm using client side database storage (WebSQL i call it). 'dbInterface' an instance of a simple javascript object I created which handles the basics of interacting with a client side database [shown at the end of this question]. the executeSql method takes up to 4 arguments
The SQL String
an optional arguments array
an optional onSuccess handler
an optional onError handler (not used in this example)
What I need to happen is: When the WebSQL query has completed, it takes some of that data and manipulates some attribute of a particular wedge. But, when I call 'logLabel' on an instance of WedgePath inside the onSuccess handler, I get the label of the very last instance of WedgePath that was created way back in the plugin code.
Now I suspect that the problem lies in the var newWedge = new WedgePath(canvas); line. So I tried pushing each newWedge into an array, which I thought would prevent that line from replacing or overwriting the WedgePath instance at every iteration...
wedgeArray = [];
// Inside the plugin...
for(var i = 1; i <= 30; i++){
var newWedge = new WedgePath(canvas);
newWedge.label = "my_wedge_"+i;
wedgeArray.push(newWedge);
}
for(var i = 0; i < wedgeArray.length; i++){
wedgeArray[i].logLabel()
}
But again, I get the last instance of WedgePath to be created.
This is driving me nuts. I apologise for the length of the question but I wanted to be as clear as possible.
END
==============================================================
Also, here's the code for dbInterface object should it be relevant.
function DatabaseInterface(db){
var DB = db;
this.sql = function(sql, arr, pSuccessHandler, pErrorHandler){
successHandler = (pSuccessHandler) ? pSuccessHandler : this.defaultSuccessHandler;
errorHandler = (pErrorHandler) ? pErrorHandler : this.defaultErrorHandler;
DB.transaction(function(tx){
if(!arr || arr.length == 0){
tx.executeSql(sql, [], successHandler, errorHandler);
}else{
tx.executeSql(sql,arr, successHandler, errorHandler)
}
});
}
// ----------------------------------------------------------------
// A Default Error Handler
// ----------------------------------------------------------------
this.defaultErrorHandler = function(transaction, error){
// error.message is a human-readable string.
// error.code is a numeric error code
console.log('WebSQL Error: '+error.message+' (Code '+error.code+')');
// Handle errors here
var we_think_this_error_is_fatal = true;
if (we_think_this_error_is_fatal) return true;
return false;
}
// ----------------------------------------------------------------
// A Default Success Handler
// This doesn't do anything except log a success message
// ----------------------------------------------------------------
this.defaultSuccessHandler = function(transaction, results)
{
console.log("WebSQL Success. Default success handler. No action taken.");
}
}
I would guess that this is due to that the client side database storage runs asynchronous as an AJAX call would. This means that it doesn't stops the call chain in order to wait for a result from the invoked method.
As a result the javascript engine completes the for-loop before running the globalFunction.
To work around this you could perform the db query inside a closure.
function getDataForIndicatorAndRegion(indicator_id, region_id, pWedge){
return function (targetWedge) {
someSql = "SELECT dataRows.status FROM dataRows WHERE indicator_id = ? AND region_id = ?";
dbInterface.sql(someSql, [indicator_id, region_id], function(transaction, result) {
targetWedge.changeColor(randomHex());
});
}(pWedge);
}
This way you preserve pWedge for each execution. Since the second method is invoking it self and send what pWedge is right now as an argument.
EDIT: Updated the code from comments. And made a change to it. The callback function maybe shouldn't be self invoked. If it invoke it self the result of the function is passed as a argument. Also if it doesn't work, try passing the other arguments.
i suspect your problem is the modifed closure going on inside globalFunction:
function(transaction, result){
targetWedge.logLabel();
})
read this