jQuery .when() is getting ignored in a loop - javascript

I have a custom loop that needs to execute a function before going into the next iteration. This is the code:
function customIteration(arr, i)
{
if (i==arr.length) return;
var message = arr[i];
jQuery('#stepTwo #num1StepTwo').html('Expires: ' + $num1);
jQuery('#stepTwo #num2StepTwo').html(jQuery(message).find('.num2').text());
jQuery('#stepTwo #num3StepTwo').html(jQuery(message).find('.num3').text());
i++;
jQuery.when(mySpecialFunction()).then(customIteration(arr, i));
}
mySpecialFunction():
function mySpecialFunction(){
return jQuery.ajax({
url: "https://api.site.com/customurl",
dataType: "jsonp",
data: { data1: $data1, data2: $data2 },
success: function (data) {
...some code...
},
error: function (e) {
...some other code...
}
});
}
problem is, I see in Fiddler that the url is being hit immediately by all the instances of the loop above, without waiting to get a response from the ajax code inside mySpecialFunction(). This is of course messes up the results I should get.

Try using .done()
in fact,
.done() has only success callback.
.then() has both success and fail callbacks.
As of jQuery 1.8, the deferred.then() method returns a new promise that can filter the status and values of a deferred through a function, replacing the now-deprecated deferred.pipe() method.
The deferred.done() method accepts one or more arguments, all of which can be either a single function or an array of functions.
Since deferred.done() returns the deferred object, other methods of the deferred object can be chained to this one, including additional .done() methods. When the Deferred is resolved, doneCallbacks are executed using the arguments provided to the resolve or resolveWith method call in the order they were added.

Try it using .done() and a recursive function, should be easier to implement and understand.
Like this:
(function recursive(arr,i){
jQuery.ajax({
url: "https://api.site.com/customurl",
dataType: "jsonp",
data: { data1: $data1, data2: $data2 },
success: function (data) {
...some code...
},
error: function (e) {
...some other code...
}
}).done(function(data){
var message = arr[i];
jQuery('#stepTwo #num1StepTwo').html('Expires: ' + $num1);
jQuery('#stepTwo#num2StepTwo').html(jQuery(message).find('.num2').text());
jQuery('#stepTwo #num3StepTwo').html(jQuery(message).find('.num3').text());
if(i!=arr.length){
recursive(++i);
}
}); /// End done
})(0); ///End recursive function
What this does is make sure your single iteration ends before calling itself up again and continuing iterating.
So basically your function is calling itself when it's done with a single iteration, and continues until everything has been iterated, then stops.

Related

How to Execute code after function is complete

I have a code where basically I've created a function in which, by the help of jQuery ajax, I fetch a value and set it as a data attribute of an element.
then after calling the function, I store the data value in a variable.
But the problem is that I don't want to execute any code before the ajax function completes.
JS
function load_data(){
$.ajax({
.....,
success: function (response) {
$('.element').attr('data-foo', 'bar')
}
})
}
load_data(); //let the function set data first
console.log($('.element').data('foo')) //then execute this line
How to achieve this?
You can receive a callback function in load_data and execute it in the success function. Something like this:
function load_data(callback){
$.ajax({
.....,
success: function (response) {
$('.element').attr('data-foo', 'bar');
callback();
}
})
}
load_data(function() {
console.log($('.element').data('foo'));
});
Of course, if this is your real scenario, you could simply put console.log($('.element').data('foo')); directly inside the success function.
You can use, Async-Await. https://javascript.info/async-await
async function load_data(){
await $.ajax({
.....,
success: function (response) {
$('.element').attr('data-foo', 'bar')
}
});
console.log($('.element').data('foo')) //then execute this line
}
load_data(); //let the function set data first
Also, you can do it using callback as well.
function load_data(callback){
$.ajax({
.....,
success: function (response) {
$('.element').attr('data-foo', 'bar');
callback();
}
})
}
function doItLater(){
console.log($('.element').data('foo')) //then execute this line
}
load_data(doItLater); //let the function set data first
You can pass the data as a parameter to doItLater for getting data to your current scope.
try using .done()
load_data()
.done(function(dataResponse){
console.log($('.element').data('foo')) //then execute this line
}); //let the function set data first
the code inside will run after a your ajax call is responded and dataResponse has any response from you ajax call
This is not directly possible due to Asynchronous ajax call.
There are two ways to achieve what you want
1.
Create a function/s for the code you want to execute after successful ajax request
Call that function/s from ajax success block
Eg.
function load_data(){
$.ajax({
.....,
success: function (response) {
$('.element').attr('data-foo', 'bar');
console.log($('.element').data('foo')); // Your code goes here
}
})
}
2
Return $.ajax in load_data function
And use .then or .done function for load_data itself
Eg.
function load_data(){
return $.ajax({
.....,
success: function (response) {
$('.element').attr('data-foo', 'bar')
}
})
}
load_data().done(function() {
console.log($('.element').data('foo')); // Your code goes here
});
However, .done and .then do not behave the same. In particular, if a standard promise callback returns another promise, this will delay the resolution of all later promises. jQuery's then behaves like this, but done does not. It is not possible for a done callback to delay the resolution of later callbacks.
Ajax is asynchronous. So, you have to listen for success event and do your stuff. You might achieve something similar to your needs by using await/async
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/async_function
const load_data = async () => {
return await fetch('https://exmaple.com');
}
load_data();
$('.element').attr('data-foo', 'bar')
You could just put the console log inside of the success callback, after you set the element attribute.
If you don't want the console log written inside load_data (maybe you want to make load_data reusable, without the console log), you could put the console log in a callback function and pass the callback to load_data and call it in the ajax success function:
function load_data(cb){
$.ajax({
.....,
success: function (response) {
$('.element').attr('data-foo', 'bar')
cb();
}
})
}
function callback() {
console.log($('.element').data('foo'))
}
load_data(callback);
Another option is to set async: false in the ajax settings object, which would prevent following code from being executed before the ajax function resolves, but this is not generally a recommended practice.
with this:
$.when(load_data()).then(console.log($('.element').data('foo')));

use a callback function before a .done() function in ajax call promises

Here is schematically what I am trying to achieve :
function loadLotsofSomething() {
for (var i=0;i<1000;i++) {
results.push(loadOneSomething(i))
}
$.when.apply(this,results).done(function() {
for (var i = 0; i < arguments.length; i++) {
//get doSomehting(data) instead of data here
}
}
}
function loadOneSomething(i) {
return $.ajax({
async : true,
url: url,
success: function(data){
return doSomething(data);
}
});
}
function doSomething (x) {
return x;
}
I would want the success function to execute before the done function because It's simpler to modify the data I get from the ajax call before looping through all the calls.
In my case, no matter what I put into the success function, I always get the raw data in the done function.
Any help welcome !
You want to use chaining (as is often the case with promises). Don't use success at all; instead, use then and return its result:
function loadOneSomething(i) {
return $.ajax({
async : true,
url: url
}).then(function(data){
return doSomething(data);
});
}
That way, the promise returned by loadOneSomething is the one from then, not the one from $.ajax, and its resolution value is what you return from then.
More: The jqXHR entry in $.ajax, deferred.then.

jQuery.when() not working as expected

I have a series of asynchronous actions whose responses must be synchronized in order to run a final action. I'm using The Deferred object and the when() and done() methods to achieve this, but for some reason, the final action within done() is always executed when the first resolve() of the responses is invoked.
This is the code I have, cut a bit short to make it clearer:
// Central function that creates a Deferred object and returns a
// Promise from it. when the passed request is done,
// the Deferred is resolved
var getData = function(url, successFunction) {
var deferred = $.Deferred();
$.ajax({
url: url,
method: 'get',
dataType: 'json'
}).done(function(p) {
successFunction(p);
deferred.resolve(p);
}).fail(function(p){
deferred.reject(p);
});
return deferred.promise();
};
// Success actions to be called for each asynchronous request
var populateDestinations = function(data) {
// synchronous actions on DOM
};
var populateTaxes = function(data) {
// synchronous actions on DOM
};
var populatePayment = function(data) {
// synchronous actions on DOM
};
// Actions that return a Promise to control the resolution of
// all the deferred objects
var getCustomerDestinations = function(customerId) {
var url = $modal.data('url_destinations') + customerId;
return getData(url, populateDestinations);
};
var getCustomerTaxes = function(customerId) {
var url = $modal.data('url_taxes') + customerId;
return getData(url, populateTaxes);
};
var getCustomerPayment = function(customerId) {
var url = $modal.data('url_payment') + customerId;
return getData(url, populatePayment);
};
var populateFields = function() {
// final actions
};
$.when(getCustomerDestinations(customer_id),
getCustomerTaxes(customer_id),
getCustomerPayment(customer_id))
.done(function(){
populateFields();
});
populateFields() is being called whenever one of the "promised" functions is resolved, not when all of them are resolved.
Any idea what I'm doing wrong? Maybe I haven't yet grasped the concept of the Deferred object.
you really dont need to use any deferred objects to track ajax calls, instead you can just use the promise object returned from $.ajax within $.when().
JQUERY CODE:
var getData = function(url, successFunction) {
return $.ajax({
url: url,
method: 'get',
dataType: 'json'
}).then(function(p) {
successFunction(p);
},function(p){
//process error using error callback,
//just like success callbacks
});
};
I order to process individual ajax calls you can use .then() instead of .done() & .fail(), because these will not return any promise object unlike .then() to track the same in .when().
Rest of your code will work as it is.
What jQuery forum says:
As of jQuery 1.8, the deferred.then() method returns a new promise that can filter the status and values of a deferred through a function, replacing the now-deprecated deferred.pipe() method. The doneFilter and failFilter functions filter the original deferred's resolved / rejected status and values. The progressFilter function filters any calls to the original deferred's notify or notifyWith methods. These filter functions can return a new value to be passed along to the promise's .done() or .fail() callbacks, or they can return another observable object (Deferred, Promise, etc) which will pass its resolved / rejected status and values to the promise's callbacks.
refence links :
http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
http://api.jquery.com/deferred.then/

Promises call their success callback too late

I have a code as follows:
products.SaveApplications = function (product) {
var promises = [];
ko.utils.arrayForEach(product.Applications(), function (item) {
var applicationToSend = ko.mapping.toJS(item);
promises.push($.ajax({
type: "POST",
contentType: "application/json",
url: serviceUrl + "/InsertApplication",
data: JSON.stringify(applicationToSend),
success: function (data) {
alert("Doing success callback now.");
item.ID(data);
}}));
});
return promises;
}
Then a function products.SaveEnvironments, which is basically the same, except it calls a different function in my controller, and a function products.SaveInstallations, which is, again, mostly the same, except it needs to wait for Environments and Applications to save, because it has some dependant data (Application.ID and Environment.ID).
Now I have a function, which calls both these functions and waits for them until both have finished:
products.SaveChanges = function (product) {
// (...)
var promises = products.SaveApplications(product);
promises = promises.concat(products.SaveEnvironments(product));
$.when(promises).done(function () {
alert("we shouldn't see this before the other message!");
products.SaveInstallations(product);
});
};
The problem is that the alert box with "We shouldn't see this message before the other message!" actually appears before the "Doing success callback now", which means my ID properties are null at the time. It leads me to the conclusion that the $.when call only does the actual promises, but doesn't wait for the success callbacks to finish. How can I make it wait? Thank you.
You are using $.when incorrectly. You can't pass multiple promises to it as an array; if you do that, it treats the array just like any other non-promise value and turns it into a completed promise with the array of the still-pending promises as its result. That, of course, is not useful.
You need to use .apply instead to simulate calling $.when with each promise as a separate argument, like this:
$.when.apply(null, promises).done(function () { ... });

Increment for only after the previous interaction has been finished (callback)

I'm having a problem with callback functions in javascript. What I want to do is: loop on a for and call a function passing i as parameter. With that in mind, I have to loop to the next interaction only after the previous one has been finished. I don't know if this is a problem but inside the function I'm sending i as parameter, I have another callback function. Here is my code:
for(i=0; i<10; i++) {
aux(i, function(success) {
/*
* this should be made interaction by interaction
* but what happens is: while I'm still running my first interaction
* (i=0), the code loops for i=1, i=2, etc. before the response of
* the previous interaction
*/
if(!success)
doSomething();
else
doSomethingElse();
});
}
function aux(i, success) {
... //here I make my logic with "i" sent as parameter
getReturnFromAjax(function(response) {
if(response)
return success(true);
else
return success(false);
});
});
function getReturnFromAjax(callback) {
...
$.ajax({
url: myUrl,
type: "POST",
success: function (response) {
return callback(response);
}
});
}
jQuery's Deferred can be a bit tricky to get right. What you'll have to do is stack your promises in a chain. For example:
var
// create a deferred object
dfd = $.Deferred(),
// get the promise
promise = dfd.promise(),
// the loop variable
i
;
for(i = 0; i < 10; i += 1) {
// use `then` and use the new promise for next itteration
promise = promise.then(
// prepare the function to be called, but don't execute it!
// (see docs for .bind)
aux.bind(null, i, function(success) {
success ? doSomethingElse() : doSomething();
})
);
}
// resolve the deferred object
dfd.resolve();
for this to work, aux must also return a promise, but $.ajax already does this, so just pass it through and everything should work:
in aux:
function aux(i, callback) {
console.log('executing for `aux` with', i);
// return the ajax-promise
return getReturnFromAjax(function(response) {
callback(Boolean(response));
});
}
in getReturnFromAjax:
function getReturnFromAjax(callback) {
// return the ajax-promise
return $.ajax({
url: '%your-url%',
type: '%method%',
success: function (response) {
callback(response);
}
});
}
demo: http://jsbin.com/pilebofi/2/
I'd suggest that you'd look into jQuery's Deferred Objects and jQuery.Deferred()-method instead of making your own callback queue functions (as you are already using jQuery anyway).
Description: A constructor function that returns a chainable utility
object with methods to register multiple callbacks into callback
queues, invoke callback queues, and relay the success or failure state
of any synchronous or asynchronous function.
I don't have experience with jQuery, but your callback looks a bit fishy to me.
In plain JS I'd suggest trying something among the lines of this:
function yourMainFunction
{
function callbackHandler(result)
{
// Code that depends on on the result of the callback
}
getAjaxResults(callbackHandler);
}
function getAjaxResults(callbackHandler)
{
// Create xmlHttpRequest Handler, etc.
// Make your AJAX request
xmlHttp.onreadystatechange = function()
{
if (xmlHttp.readyState == 4 && xmlHttp.status==200)
{
// Do stuff you want to do if the request was successful
// Define a variable with the value(s) you want to return to the main function
callbackHandler(yourReturnVariable);
}
}
}

Categories

Resources