Execute dynamic number of ajax request sequentially - javascript

Have the following scenario :
I have to display the graphs for a given interval (startDate,endDate)
Because the interval might be quite big , the data is retrieved per day so I need
to do multiple ajax calls sequentially and to append the data to the graph(highcharts)
Example interval is n days ==>
ajax request day 1
when is (done) ready ajax request day 2
when is (done) ready ajax request day 3
....
ajax request day n
I read about deferred and promises BUT I found difficult to with dynamic number of days and the requirement to get the responses sequentially
Thanks

If you're able to store the list of dates in an array, you can use something like this:
var items = ['Apple', 'Orange', 'Banana', 'Alphalpha'];
//replaceable with any function that returns a promise
function asyncFunction(item) {
return $.ajax({
url: '/echo/html',
type: 'POST',
data : item
})
.then(function(data){
$('body').append('<div>Got the response from '+item+'</div>');
//stuff stuff stuff
});
}
function sequence(arr, callback) {
var i=0;
var request = function(item) {
return callback(item).then(function(){
if (i < arr.length-1)
return request(arr[++i]);
});
}
return request(arr[i]);
}
sequence(items, asyncFunction).then(function(){
$('body').append('<div>Done with all!</div>');
});
https://jsfiddle.net/7ojy9jnx/2/
Basically, sequence takes an Array of items and runs a function on all of them (in this case asyncFunctions, which can be replaced with any function), a function that returns a promise.
This is very basic implementation, you'll notice, for example, it has no error handling. Libraries like async.js have an exhaustive list of tools that accomplish tasks like this, but who knows, maybe this will suffice.

Not sure if you already figured it out, but a good way to tackle your problem would be using a combination of jQuery.Deferred and recursion. Check out this sample code and see if it helps clarify things:
function getData(dayLimit) {
var allDone = $.Deferred();
var getDataForDay = function(day) {
doAsyncThing(day).done(function() {
if (day < dayLimit) {
getDataForDay(day + 1);
} else {
allDone.resolve();
}
}).fail(function(){
/*
Reject the deferred if one of your operations fails.
Useful if you're binding "fail" or "always" callbacks
to the promise returned by getData.
*/
allDone.reject();
});
};
getDataForDay(1); //start with first day
return allDone.promise();
}
Let me know if you need more clarification, happy to help!

What about recursively calling. Create a parameterized function and pass the day to the function like,
function getDetails(day) {
// ajax call
// In the callbacks call the getDetails function by updating the date
}

If you are using Jquery in your Application try pushing all the ajax to an array
ex
[ajax,ajax,...]
and then user
$.when([ajax,ajax,...]).then(function(){
console.log(arguments);// you will get the success messages in arguments array
})

Related

Wait for list of functions to complete then reload page

I have lots of different functions that send AJAX requests to save different parts of the page. When the user clicks save, All these functions are run like so.
function savePage() {
if (!confirm('Save changes?')) return false;
saveSortOrder();
saveAllWidth();
saveAllTinyMCE();
saveAllWidgetRm();
location.reload();
}
Once everything is saved, I want to reload the page but location.reload() runs before everything is finished.
A typical save function looks like this but some are much bigger and there are lots of them
function saveAllPublic() {
$('.widget').each( function(){
var parentID = $(this).attr('id');
var publicState = $(this).attr('data-public');
$.post('widgets/manage_widgets.php', {
update: 'publicity',
wd_parent: parentID,
public: publicState
});
});
}
Basically, I want all the POSTs to complete before reloading the page.
First of all, you will want the save functions to return an array of promises. We can do that by simply using .map(), and within in return the AJAX call you have made, i.e.:
// saveAllPublic() will return an array of promises
function saveAllPublic() {
return $('.widget').map(function(){
var parentID = $(this).attr('id');
var publicState = $(this).attr('data-public');
return $.post('widgets/manage_widgets.php', {
update: 'publicity',
wd_parent: parentID,
public: publicState
});
}).get();
}
Note: Remember to use .get() at the end of .map(), in order to obtain a true array and not a jQuery collection (which is an array-like object). See a more thorough explanation here: map() get() confusion
When you want to check if all the requests made by saveAllPublic() is done, you can simply do this:
var saveAllPublicAJAX = saveAllPublic();
$.when.apply($, saveAllPublicAjax).then(function() {
// Callback when all POST requests in saveAllPublic() is completed
});
And let's say based on your example you have refactored all the save functions to use the array push method I have mentioned above, you can simply concatenate all these returned arrays into a single one, and pass it to $.when:
function savePage() {
if (!confirm('Save changes?')) return false;
var saveAJAX = [].concat.apply([], [
saveSortOrder(),
saveAllWidth(),
saveAllTinyMCE(),
saveAllWidgetRm()
]);
$.when.apply($, saveAJAX).then(function() {
// When all the requests are successful
location.reload();
}, function() {
// When one or more requests have failed
// ...
});
}
There are, of course, other more verbose way of constructing the array, such as:
var saveAJAX = saveSortOrder().concat(saveAllWidth()).concat(...)
var saveAJAX = []; saveAJAX.push(saveSortOrder()); ...

Return an Array from an Async call, then additional Async calls for each element of the array

I'm writing an application in javascript where I make a CORS request to the server to grab a data array.
Then, for each item in the array, I need to make another CORS call to get additional info on that element.
I originally thought I could return values from my CORS request like:
data = getData(param);
But apparently you can't mix synchronous and asynchronous code.
What's the best way to accomplish this?
Promises. Here's how you might use them using your requirements, and a setTimeout to mimic an AJAX request.
getData returns a new promise. In this case if the function is called with no params an array is sent back after a second (your first request). If a param is passed into the function 100 is added to the param before resolving - the later requests.
function getData(param) {
return new Promise(function(resolve, reject) {
if (param) {
setTimeout(() => resolve(param + 100), 500);
} else {
setTimeout(() => resolve([1, 2, 3, 4, 5]), 1000)
}
});
}
Call getData without a param and [1, 2, 3, 4, 5] is returned. then we map over the array elements and return new promises for each of them. then we use Promise.all to resolve those promises and then we output the final array [101, 102, 103, 104, 105].
getData()
.then((arr) => arr.map(el => getData(el)))
.then(arr => Promise.all(arr))
.then(arr => console.log(arr));
DEMO
So you can see that you can run one AJAX request and then run more based on the result of the value that's returned until all requests have been made.
You can use async.series. checkout https://github.com/caolan/async . Very good library to solve problem like this - process an array data asynchronously(My favourite).
Or
You can use js promise from https://www.promisejs.org/
Or play with callbacks... like below
Note: Below functions are indicative functions just to show how you can approach the problem as you haven't shared any code. Change them accordingly. Also there might be syntactical/spell error as the code is written directly here.
function ajaxRequester(method,uri, data, onSuccess, onError){ // you can make this function as per requirement.
$.ajax({
type: method,
url: uri,
data: data
success: function(response){
onSuccess(response);
}
});
}
function yourFunction(){
ajaxRequester('GET',urlOf1stRequest,dataToSend,function(resp){
// assuming resp is the array received from server. we'll start with 0th element
processArray(0,resp, function(){
// do your final stuff
});
});
}
function processArray(index, arr, onComplete){
if(index < arr.lenght){
var objToProcess = arr[index]; // get your data to process
ajaxRequester(yourMethod,obj.url, obj.data, function(resp){
// do work with your response variable resp
processArray(++index, arr); // process next element of array after completion of current
});
} else {
onComplete(); // all elements are processed call final callback
}
}

Async request into for loop angular.js

I have an array and i need to send values of array to webservice through http post request one by one . For the node.js , i'm using "async" package to do that for ex: async.eachSeries doing it well , how can i do that same thing for angular.js , my normal async code;
//this code sends all queries of array (maybe 5.000 request at same time , it is hard to process for webservice :=) ) at same time and wait for all responses.
//it works but actually for me , responses should wait others at end of loop should work one by one
//like async.eachSeries module!
for (var i = 0; i < myArr.lenght; i++) {
(function (i) {
var data = {
"myQuery": myArr[i].query
};
$http.post("/myServiceUrl", data).success(function (result) {
console.log(result);
});
})(i);
}
Both Matt Way and Chris L answers Correct , you can investigate Chris's answer for understanding about async to sync functions in for loops.
You can use $q to create a similar requirement by chaining promises together. For example:
var chain = $q.when();
angular.forEach(myArr, function(item){
chain = chain.then(function(){
var data = {
myQuery: item.query
};
return $http.post('/myServiceUrl', data).success(function(result){
console.log(result);
});
});
});
// the final chain object will resolve once all the posts have completed.
chain.then(function(){
console.log('all done!');
});
Essentially you are just running the next promise once the previous one has completed. Emphasis here on the fact that each request will wait until the previous one has completed, as per your question.
function logResultFromWebService(value)
{
$http.post("/myServiceUrl", value).success(console.log);
}
angular.forEach(myArray, logResultFromWebService);
If I understand your question correctly. You want to run a for loop in a synchronized manner such that the next iteration only occurs once the previous iteration is completed. For that, you can use a synchronized loop/callbacks. Especially if the order matters.
var syncLoop = function (iterations, process, exit) {
var index = 0,
done = false,
shouldExit = false;
var loop = {
next: function () {
if (done) {
if (shouldExit && exit) {
return exit(); // Exit if we're done
}
}
// If we're not finished
if (index < iterations) {
index++; // Increment our index
process(loop); // Run our process, pass in the loop
// Otherwise we're done
} else {
done = true; // Make sure we say we're done
if (exit) exit(); // Call the callback on exit
}
},
iteration: function () {
return index - 1; // Return the loop number we're on
},
break: function (end) {
done = true; // End the loop
shouldExit = end; // Passing end as true means we still call the exit callback
}
};
console.log('running first time');
loop.next();
return loop;
}
For your particular implementation:
syncLoop(myArray.length, function (loop) {
var index = loop.iteration();
var data = {
"myQuery": myArray[index].query
};
$http.post("/myServiceUrl", data).success(function (result) {
console.log(result);
loop.next();
});
}, function () {
console.log('done');
});
If you intend on doing something with the data once returned (such as perform calculations) you can do so with this method because you will return the data in a specified order.
I implemented something similar in a statistical calculation web app I built.
EDIT:
To illustrate the problem I had when using $q.when I have set up a fiddle. Hopefully this will help illustrate why I did this the way I did.
https://jsfiddle.net/chrislewispac/6atp3w8o/
Using the following code from Matt's answer:
var chain = $q.when(promise.getResult());
angular.forEach(myArr, function (item) {
chain = chain.then(function () {
$rootScope.status = item;
console.log(item);
});
});
// the final chain object will resolve once all the posts have completed.
chain.then(function () {
console.log('all done!');
});
And this fiddle is an example of my solution:
https://jsfiddle.net/chrislewispac/Lgwteone/3/
Compare the $q version to my version. View the console and imagine those being delivered to the user interface for user intervention in the process and/or performing statistical operations on the sequential returns.
You will see that it does not sequentially give the numbers 1,2,3,4 etc. either in the console or in the view in Matt's answer. It 'batches' the responses and then returns them. Therefore, if step 3 is not to be run depending on the response in step 2 there is not, at least in the answer provided, a way to break out or explicitly control the synchronous operation here. This presents a significant problem when attempting to perform sequential calculations and/or allow the user to control break points, etc.
Now, I am digging through both the $q libraries and the Q library to see if there is a more elegant solution for this problem. However, my solution does work as requested and is very explicit which allows me to place the function in a service and manipulate for certain use cases at my will because I completely understand what it is doing. For me, that is more important than using a library (at least at this stage in my development as a programmer and I am sure there are lots of other people at the same stage on StackOverflow as well).
If the order doesn't matter in which they are sent
var items = [/* your array */];
var promises = [];
angular.forEach(items, function(value, key){
var promise = $http.post("/myServiceUrl", { "myQuery": value.query });
promises.push(promise);
});
return $q.all(promises);

AngularJs :How to execute the functions in a synchronous fashion

How to execute three services in a synchronous way in AngularJS? I have three $http.get() services and on its success have to read the JSON fields and if the particular set of fields have valid data,a flag has to be set to true/false and depending on the flag result the next service will be called otherwise not.But here, the services are running asynchronously so my logic is failing.
Sample Code:
// Condition 1
if(item === false) {
var product = Service1.get().then(function (response) {
// Logic for reading the JSON
// Setting the flag based on it..
item = true/false;
}
//Condition 2
if(item === false) {
var call = Service2.get().then(function (data) {
// Logic for reading the JSON
// Setting the flag based on it..
item = true/false;
}
}
// Condition 3
if(item === false) {
var product = Service3.get().then(function (response) {
// Logic for reading the JSON
// Setting the flag based on it..
item = true/false;
}
}
}
Here, the problem is that code in *Condition3* is getting executed first then code in *Condition1* and *Condition2* which is causing the unexpected results.
It would be of great help if someone has the sample code in which three services are executed in a sequential manner.
Instead of executing a new $http request in the success handler and write cascades of requests, perhaps you can solve it in a recursive way:
function recursiveHttp(array) {
$http(array[0].config).then(function() {
array[0].success();
recursiveHttp(array.splice(0,1));
}, function() {
array[0].error();
recursiveHttp(array); //beware: no escape clause
});
}
Where the array is a collection of objects that contain the required config object and two callback functions.
{
config : {
method: 'get',
url: 'myurl'
},
success: function() {
//do stuff
},
error: function() {
//do stuff
}
}
There are 2 ways to achieve what you want (as far as i can make out from your question):
change the behavior of $http.get to async by using async : true.
chain your requests properly so that one executes only after the other, so that each has their dependencies met before they start executing. this ca be done by calling the dependent function on the callback of the first one.

how to wait until Array is filled (asynchronous)

I have an array which is filled asynchronous and contains 28 items. I want to wait until the array is filled with all items.
function checkIfFinished(){
return(Results.length >= 28);
}
var isfinished = false;
while(isfinished){
if(checkIfFinished()){
returnResults();
isfinished = true;
}
else
//Wait 100ms
}
Well, but in Javascript there is no wait function! I tried it with setTimeout, but I don't know how to insert it... I just get errors with too much recursion and stuff :D
Thank you!
Try:
var timeout = setInterval(function() {
if(checkIfFinished()) {
clearInterval(timeout);
isFinished = true;
}
}, 100);
This will call your check-function every 100 ms until checkIfFinished() gives true back to you.
If you're using jQuery 1.5+, this sounds like a perfect opportunity to use deferred objects and promises in your code. I'm assuming that you're using AJAX calls to populate your array.
In a nutshell, something like this should work for you:
$(function() {
var $ajaxcalls = [],
myArray = [];
// set up all the ajax calls that will populate my array
for(var i=0; i < 28; i++) {
$ajaxcalls[i] = $.ajax({
url : 'http://your.domain.com/blah',
data : i
}).success(function(m) {
myArray.push(m);
});
}
// this will setup the promise ---
// what will run when all 28 AJAX calls complete?
$.when.apply(null, $ajaxcalls).then(function() {
returnResults();
});
});
I've written about this some time back as well. I really think it's a nifty feature / concept that can be really powerful when used correctly. Javascript timers and schedules should work as well, but they can be unwieldy and may result in a bit of wait time before the actual completing logic fires.

Categories

Resources