jquery ajax when - parallel ajax call based on condition - javascript

Suppose that i have multiple ajax request that need to be called parallel.
The ajax request that need to be called is based on condition.
I can do it as below but it seems not feasible and trivial.
if (condition1) {
$.when(
apiRequest1();
).then(function(result1) {
});
} else if (condition 2) {
$.when(
apiRequest2();
).then(function(result1) {
});
} else if (condition 1 && condition 2) {
$.when(
apiRequest1();
apiRequest2();
).then(function(result1, result2) {
});
}
What i want to achieve is as follow. Concept is as follow but how can it be done?
var apiList = [];
if (condition1) {
append apiRequest1() to apiList;
}
if (condition2) {
append apiRequest2() to apiList;
}
if (condition3) {
append apiRequest3() to apiList;
}
if (conditionN) {
append apiRequestN() to apiList;
}
if (apiList has value) {
$.when(
apiList
).then(function (resultN) {
});
}

Arrays can handle that perfectly fine.
In each if () block, just append your promise to the array:
apiList.push(apiRequestN());
At the end, you need to pass each item as a separate parameter to $.when():
$.when.apply(null, apiList)

Try maybe this solution:
var async1 = $.ajax({//call 1
url:'http://mypage.com',
success: function(data1){
//data 1
}
});
....
var async2 = $.ajax({//call 2
url:'http://mypage.com',
success: function(data2){
//data2
}
});
$.when(async2, async1).done(function(result2, result1) {
console.log('done!')
});

var apiList = [];
if (condition1) {
apiList.push(apiRequest1());
} else if (condition 2) {
apiList.push(apiRequest2());
} else if (condition 1 && condition 2) {
apiList.push(apiRequest1());
apiList.push(apiRequest2());
}
Promise.all(apiList).then((result1, result2) => {
//Do your thing with the results
})

Related

Angular - For Loop HTTP Callback/Promise

I am trying to write a loop which performs a number of http requests and adds each response to a list.
However, I don't think I am going about it quite the right way.
I think I am not implementing the required promises correctly. The console log after the for loop shows myList array as empty.
Code:
var _myList = []
function getStuff() {
var deferred = $q.defer()
var url = someUrl
$http.get(url).success(function(response) {
if ( response.array.length > 0 ) {
// loop starts here
for ( var i=0; i < response.array.length; i++ ) {
getThing(response.array[i].id);
};
// check the varibale here
console.log(_myList);
deferred.resolve('Finished');
} else {
deferred.resolve('No stuff exists');
};
}).error(function(error) {
deferred.reject(error);
});
return deferred.promise;
};
function getThing(thindId) {
var deferred = $q.defer()
var url = someUrl + thingId;
$http.get(url).success(function(response) {
_myList.push(response);
deferred.resolve(response);
}).error(function(error) {
deferred.reject(error);
});
return deferred.promise;
};
You can simplify your code as follows:
var allThings = response.array.map(function(id){
var singleThingPromise = getThing(id);
//return a single request promise
return singleThingPromise.then(function(){
//a getThing just ended inspect list
console.log(_myList);
})
});
$q.all(allThings).then(function(){
//only resolve when all things where resolved
deferred.resolve('Finished');
}, function(e){
deferred.reject('Something went wrong ' + e);
});
You indeed won't be able to populate _myList array with for-loop like you set up. Instead create an array of promises - one per data item in response.array and return it as inner promise.
function getStuff() {
var url = someUrl;
return $http.get(url).then(function(response) {
if (response.data.array.length > 0) {
return $q.all(response.data.array.map(function(data) {
return getThing(data.id);
}));
} else {
return 'No stuff exists';
}
});
}
function getThing(thindId) {
var url = someUrl + thingId;
return $http.get(url).then(function(response) {
return response.data;
});
}
After that you would use getStuff like this:
getStuff().then(function(myList) {
console.log(myList);
});

Function in while loop executes only once

I am a beginner in javascript, and I'm trying to figure out why my while loop won't actually loop more than once, even though the condition is always met.
I have a function sending an API request:
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true;
}
else {
console.log('False');
return false;
}
}).error(function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
I want to call this function until it returns true, so I call it this way:
var count = 0;
while (get_status(id, count) === false) {
count += 1;
}
The count variable is just added to see how many times it loops, it stays at 0 even though 'False' is displayed in the console.
Is there some behaviour I am misunderstanding here?
EDIT I understand why this won't work. My intention here is to display an iframe as long as the transaction status is pending. I thought of continually sending a request until the transaction status is something other then 'Pending', but I am aware there are more optimal ways.
Your get_status() function does not return a value. Thus, it's return value is undefined which is falsey so your while() loop stops after the very first iteration.
The return statements you do have in your code are inside of callbacks and have nothing to do with the return value of get_status().
What you are attempting to do is generally not a good design. It appears that you want to run a given Ajax call over and over with no delay until you get the answer you want. This will potentially hammer the destination server.
If you describe the problem you're really trying to solve, we could help come up with a better way to do this. Worst case, you could poll the server with a time delay between requests.
If you wanted to poll every so often, you could do something like this:
function get_status(trid, count) {
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
return $http(req).then(function(data) {
return data.transaction_status;
});
}
function poll_status(callback) {
function next() {
get_status(...).then(function(status) {
if (status === "Pending") {
// poll once every two seconds
setTimeout(next, 2000);
} else {
// status is no longer pending, so call the callback and pass it the status
callback(status);
}
}, function(err) {
callback(err);
});
}
next();
}
poll_status(function(result) {
// done polling here, status no longer Pending
});
This is not the correct way to deals with async calls, I'd create a recursive function which will call itself. (in this case get_status should return a promise)
Code
var count = 0, id = 1;//id should be some value
(function myCall(promise){}
promise.then(function(data){
count += 1;
if(data)
myCall(get_status(id, count)); //call function on conditon
});
}(get_status(id, count))
Method(Returning Promise)
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
//returning promise here
return $http(req).then(function(response) {
var data = response.data;
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true; //resolves the promise
}
else {
console.log('False');
return false; //resolves the promise
}
}, function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
You're trying to return from within an asynchronous callback, which won't work, unfortunately. Instead you'll want a module like async, specifically whilst.
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your code here, setting outcome instead of returning
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
outcome = true;
callback();
}
else {
outcome = false
callback();
}
}).error(function(data) {
outcome = true;
callback();
})
},
function (err) {
// All done!
}
);
But really the behavior you're looking for is probably checking on a status at pre-defined intervals. In this case, adapting the code
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your request stuff.
setTimeout(function () {
callback();
}, 1000); // Waits one second to begin next request
},
function (err) {
// All done!
}
);

Javascript for loop Promises

I have an array of urls like this
var urls = ["www.google.com", "www.yahoo.com"];
And I want to loop though the urls and perform an async task inside the loop and not move on to the next item until the async task has finished. I know you can do this with promises but I have having some trouble with it. Here what I have
var xmlReader = require('cloud/xmlreader.js');
function readResponse_async(xlmString) {
var promise = new Parse.Promise();
xmlReader.read(xlmString, function (err, res) {
if(err) {
promise.reject(err);
} else {
promise.resolve(res);
}
});
return promise;
}
for (i = 0; i < urls.length; i++) {
Parse.Cloud.httpRequest({
url: unionUrls[i],
}).then(function(httpResponse) {
try {
// console.log(httpResponse.text)
return readResponse_async(httpResponse.text)
} catch (e) {console.log(e)}
}
But right now it doesn't wait for the readResponse_async to finish, how can I have it wait for that?
Thanks
EDIT
After reading the response I make a save to my database and I have another array like this
var location = ['USA', 'England'];
And I make the save like this
function saveLoc_async(data, location) {
var i3, i4, i5, m,
TestItem = Parse.Object.extend("TestItem"),//can be reused within the loops?
promise = Parse.Promise.as();//resolved promise to start a long .then() chain
for (i3 = 0; i3 < data.count(); i3++) {
(function(testItem) {
testItem.set("item", data.at(i));
testItem.set("location", location);
//build the .then() chain
promise = promise.then(function() {
return testItem.save();
});
})(new TestItem());
//************************
//CALL retry(); here?
//**************************
}
Because with your answer I have
function retry() {
if (urlsUnion.length > 0) {
var nextUrl = urlsUnion.pop();
//********** ADDED LINE
var nextLoc = location.pop();
Parse.Cloud.httpRequest({
url: nextUrl,
}).then(function(httpResponse) {
xmlReader.read(httpResponse.text, function (err, res) {
if(err) {
// show an error
} else {
//********** ADDED LINE
saveLoc_async(res, nextLoc);
retry();
}
});
});
}
}
SO where should retry(); go because right now with the save sometimes it puts the second location with one of the first items url? why would that happen?
I did something similar to this for an animation.
var actions = [drawXXX, fadeOutYYY, drawXYZ];
this.startAnimation = function () {
actions.reduce(function (previousAction, nextAction) {
return previousAction.then(nextAction)
}, $.when());
}
Your code fires both urls immediately, and does not wait in-between.
What you would have to do is to remove the first url from the array and fire it. In the 'then' branch check if you still have url's in the array and repeat.
Like this (untested, edited to make the code clean again):
var xmlReader = require('cloud/xmlreader.js');
function readResponse_async(xlmString) {
xmlReader.read(xlmString, function (err, res) {
if(err) {
// show an error
} else {
readFirstUrl();
}
});
}
function readFirstUrl() {
if (urlsUnion.length == 0) {
return;
}
var url = urlsUnion.pop();
Parse.Cloud.httpRequest({
url: url,
}).then(function(httpResponse) {
readResponse_async(httpResponse.text);
});
}
readFirstUrl();
Not sure I understand your use of unionUrls array, but if you have your URL's in a urls array, I think this is pretty clean:
function getUrl(url) {
return Parse.Cloud.httpRequest(url)
.then( function(httpResponse) {
return readResponse_async(httpResponse.text);
});
}
urls.reduce( function(prev, url) {
return prev ? prev.then( function() { getUrl(url); }) : getUrl(url);
}, null);

Wait until all ajax requests are done

I need to wait until all my ajax functions are done, and then continue the exectution.
My particular case is that I need to translate some fields in a form before submitting it. I translate them with an ajax call to an external site. Depending on some values in the form i would need to do more or less translations. When all the translations are done (if any) I have to validate the form with ajax, and if its valid, then submit.
This is my aproach:
First, I have a function that sends the ajax call and do stuff with the data received:
function translate(...) {
$("#ajaxCounter").val(parseInt($("#ajaxCounter").val()) + 1);
$.ajax({
...
success:function(data) {
...
$("#ajacCounter").val(parseInt($("#ajaxCounter").val()) - 1);
}
});
Then, when the form is to be submitted I execute the following code:
$("#form").submit(function() {
translatable_fields.each(function() {
translate(...);
});
while (parseInt($("#ajaxCounter").val()) > 0) { null; }
if (!(this).hasClass('ready')) {
$.ajax({
//validation
success: function(data) {
if (data['isValid']) {
$("#form").addClass('ready');
$("#form").submit();
}
}
});
}
return true;
});
The problem is that the while loop in the submit function never ends.
If I execute the code without the while loop I can see the ajaxCounter input increasing when the translation functions start and decreasing when they end.
You can achieve this in a much neater fashion using the deferred objects returned from a $.ajax call. First you should get the translate() function to return the deferred:
function translate(...){
return $.ajax({
// settings...
});
});
Then you can put all those promises in to a single array:
var requests = [];
translatable_fields.each(function(){
requests.push(translate(...));
});
Then you can apply that array to $.when:
$.when.apply($, requests).done(function(schemas) {
console.log("All requests complete");
// do something...
});
You can do this using deferred objects, but you do not need to use $.when.apply with an array if you are only interested in the final completion.
Instead you can chain parallel promises using the pattern promise = $.when(promise, another promise)
Change your translate to return the Ajax promise:
function translate(...) {
...
return $.ajax({
...
});
}
and your promise loop simply becomes:
var promise; // Start with an undefined promise - which is the same as a resolved promise for $.when
translatable_fields.each(function() {
promise = $.when(promise, translate(...));
});
// Wait for all promises to complete
promise.done(function(){
// now do the final code after all the ajax calls complete
});
Notes:
This does create an extra promise per call to $.when, but the overhead is very small and the resulting code is quite simple.
No, you can't just loop like this: the callbacks would never get a chance to be called.
I would do something like this:
function translateAllFields(done) {
var requestsInProgress = 0, doneCalled = false;
translatable_fields.each(function () {
++requestsInProgress;
$.ajax({
//...
success: function (data) {
//...
$("#ajacCounter").val(parseInt($("#ajaxCounter").val()) - 1);
}
}).always(function () {
if (--requestsInProgress === 0) {
done();
doneCalled = true;
}
});
});
if (requestsInProgress === 0 && !doneCalled) {
// in case translatable_fields was empty
done();
}
}
and then:
$("#form").submit(function (e) {
if (!(this).hasClass('ready')) {
e.preventDefault();
e.stopPropagation();
translateAllFields(function() {
$.ajax({
//validation
success: function (data) {
if (data['isValid']) {
$("#form").addClass('ready');
$("#form").submit();
}
}
});
});
}
});
You can use callback
function translate(..., callback) {
$.ajax({
...
success:function(data) {
...
callback(data);
}
});
};
And pass your after ajax code to it
$("#form").submit(function() {
translatable_fields.each(function() {
translate(..., function(result){
if (!(this).hasClass('ready')) {
$.ajax({
//validation
success: function(data) {
if (data['isValid']) {
$("#form").addClass('ready');
$("#form").submit();
}
}
});
}
return true;
});
});
});

How to simplify my javascript code(ajax)

I use ajax in my code. I have to call connectHost() from $('SyncDataPort0') to $('SyncDataPort5'),
function connectHost()
{
ajaxFrame($('SyncDataPort0').value, getConnectStatus);
}
function getConnectStatus(transport)
{
try {
rs = transport.responseText;
if(rs == 'OK') {
//$('SyncDataState0').innerHTML = 'ok';
addStateMsg($('ConnectTest'),getMsg('msgConnectOk'));
} else //NOT OK
addStateMsg($('ConnectTest'),getMsg('msgConnectNotOkResult').replace('%s',rs));
}catch(e){alert(e)};
}
function ajaxFrame(url, pars, onCompleteFun)
{
if (3 in arguments)
addStateMsg(arguments[3],getMsg('msgDataSending'),0);
new Ajax.Request(url,
{
method:'post',
parameters:pars,
onComplete: function(transport)
{
var rs = transport.responseText;
if('logout' == rs)
location.href='/index.php?menu=logout';
else if('' == rs)
{
//do nothing
}else
onCompleteFun.apply(this,[transport]);
},
onFailure:function()
{
debug('Load Data Failure!');
}
});
return true;
}
The question is how can i implement the function without reproducing the getConnectStatus
callback function???
If you use an inline function declaration, you can refer to variables in the parent scope and you can pass the port to your connectHost() function.
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
// you can refer to portNum here in the callback
try {
rs = transport.responseText;
if(rs == 'OK') {
//$('SyncDataState0').innerHTML = 'ok';
addStateMsg($('ConnectTest'),getMsg('msgConnectOk'));
} else //NOT OK
addStateMsg($('ConnectTest'),getMsg('msgConnectNotOkResult').replace('%s',rs));
} catch(e) {alert(e)};
});
}
If you want getConnectStatus() to still be its own function, then you can use an inline stub function like this:
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
getConnectStatus(transport, portNum);
});
}
And getConnectStatus() will have the portNum as the second argument. You can pass as many arguments through to the callback as you like this way.
If getConnectStatus() needs the value of this preserved, then you would do this:
function connectHost(portNum)
{
ajaxFrame($('SyncDataPort' + portNum).value, function(transport) {
getConnectStatus.call(this, transport, portNum);
});
}

Categories

Resources