Call ajax inside loop using promise - javascript

Hi everyone I am trying to use ajax inside a loop using promise, but my second ajax call inside the loop don't way for the ajax request and continue the execution.
This is my code:
var general = [];
var all_info =[];
var usuario =[];
var resultPromise = getProjects(); // Promise for a response.
resultPromise.then(function(all_projects) {
return $.when.apply($, all_projects.map(function (current_project, index){
var items = {};
items.name = current_project.key;
items.children = [{"total_cpu": current_project.cpuhour_tot, "num_jobs" : current_project.num_jobs }];
return addUsers(current_project.key)
.then(function(item_user) {
info_user = {};
info_user.name = item_user.key;
info_user.children = [{"total_cpu" : item_user.cpuhour_tot, "num_jobs": item_user.num_jobs }];
all_info.push(info_user);
});
items.children.push(all_info);
general.push(items)
}));
})
.then(function() {
console.log("complete", general);
})
.fail(function(jqxhr, textStatus, errorThrown) {
console.log(errorThrown);
})
when I return from this line return addUsers... I need to include the all_info values to items and before to execute other loop to all_projects I have to do that general.push(items)
but it is impossible to access to items.
What I am missing?
Thanks in advance!

Substitute .map() for .forEach() use $.when(), Function.prototype.apply(). usuario is an array; .push() value to the array.
resultPromise.then(function(all_projects) {
return $.when.apply($, all_projects.map(function (current_project, index){
var items = {};
items.name = current_project.key;
items.children = [{"total_cpu": current_project.cpuhour_tot, "num_jobs" : current_project.num_jobs }];
return addUsers(current_project.key)
.then(function(value) {
console.log(value)
usuario.push(value);
// use value here before continues;
// do stuff with `value` or `usuario` here
});
}));
})
.then(function() {
console.log("complete", usuario)
})
.fail(function(jqxhr, textStatus, errorThrown) {
console.log(errorThrown)
})

Reading about the asynchronous and single threaded nature of javascript would help you understand what happens here.
Essentially the following instruction:
resultPromise.then(function(value) {
usuario = value;
// use value here before continues;
console.log("first");
});
Is asking "Execute the callback function at some point in the future when the promise is resolved and execution stack is cleared". The callback function doesn't run in place, and actually won't run until after the current execution thread completes.

Related

javascript wait for multiple async ajax response is not working

I have been trying to call multiple ajax call in async mode and then waiting for all the ajax calls to complete before proceeding.
I am using jquery .when().
var results_array = [];
var num = 0;
var promises = [];
ldap_cmd_array.forEach(element => {
var myldap = ldap_data;
myldap.push({
"name": "cmd",
"value": element
});
console.log(++num);
promises.push(ajaxCall(myldap, 'aaa',
// success callback
function (data) {
console.log(--num);
results_array.push(data);
console.log('pass');
},
//error callback
function (err) {
//Do nothing
console.log(--num);
console.log('fail');
}
));
});
$.when.apply($, promises)
.then(function() {
console.log(results_array);
});
But in the output, I see that results_array is printing before all the ajax call is completed. I'm not sure where I am going wrong. Need help? Thanks in advance.
Note: output image is attached.
Chrome browser console output
Finally I was able to solve it.
Thanks to Zim84, your pointer actually solved my problem, kudos!!
var results_array = [];
var num = 0;
var promises = [];
console.log(ldap_data);
ldap_cmd_array.forEach(element => {
var myldap = ldap_data.slice(); //to copy a javascript object
myldap.push({
"name": "cmd",
"value": element
});
var dObject = new $.Deferred();
console.log(++num);
promises.push(dObject);
ajaxCall(myldap, 'taaa',
// success callback
function (data) {
console.log(--num);
dObject.resolve();
results_array.push(data);
console.log('pass');
},
//error callback
function (err) {
//Do nothing
dObject.resolve();
console.log(--num);
console.log('fail');
}
);
});
$.when.apply($, promises)
.then(function () {
console.log('I should print after all promises');
console.log(results_array);
});
Promise.all([ajaxCall1, ..., ajaxCallN]).then(responseArray => {})

How to execute multiple ajax calls and get the result?

Hi I want to execute a batch of ajax calls and get the response and then render the results for the user.
I'm using this code but it is not working because the render function executes before all the ajax responses have been collected.
serviceQuery: function (id) {
return $.getJSON(SERVICEURL + "/", id);
},
queryService: function(data){
var self = this;
var queries = [];
var results = [];
$.each(data, function (index, value) {
queries.push(self.serviceQuery(value.id));
});
$.when(queries).done(function (response) {
$.each(response, function (index,val) {
val.then(function (result){
results.push(result[0]);
});
});
self.renderResult(results);
});
},
renderResult: function(results){
$.each(results, function (index, value) {
///Error here cause the value.Name is undefined
console.info(value.name);
});
}
Any Idea on how to wait for all the ajax calls to finish before execute the render function?
Use .apply() at $.when() call to handle an array of Promises. Note also that .then() returns results asynchronously
let queries = [
// `$.ajax()` call and response
new $.Deferred(function(dfd) {
setTimeout(dfd.resolve, Math.floor(Math.random() * 1000)
// response, textStatus, jqxhr
, [{name:"a"}, "success", {}])
})
// `$.ajax()` call and response
, new $.Deferred(function(dfd) {
setTimeout(dfd.resolve, Math.floor(Math.random() * 1000)
// response, textStatus, jqxhr
, [{name:"b"}, "success", {}])
})
];
$.when.apply(null, queries)
.then(function() {
renderResult($.map(arguments, function(res) {return res[0]}));
});
function renderResult(results) {
$.each(results, function (index, value) {
console.info(value.name);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
Change the $.each to a for loop. It is possible that the .then on the value isn't finished processing before the loop completes. $.each is synchronous but the .then generally, means it's a promise and isn't synchronous.
$.each(response, function (index,val) {
val.then(function (result){
results.push(result[0]);
});
});
Change too
for(var idx = 0; idx < response.length; idx++) {
results.push(response[idx]);
}
or keeping with the each you may just need to remove the .then call.
$.each(response, function (index,val) {
results.push(val[0]);
});
I see one potential issue here:
$.when(queries).done(function (response) {
$.each(response, function (index,val) {
val.then(function (result){
results.push(result[0]);
});
});
self.renderResult(results);
});
Basically, your pseudocode says this:
For each return value from your $.when() command, take the value of that and return a new promise (via val.then). However, because you never wait for the val deferred to run, results.push is not guaranteed to be called before your self.renderResult(results) call.
The code looks weird to me in that you would need two nested deferreds for an ajax call. So I think a larger meta issue is why do you need to do val.then in the first place. However, given the current code, you would need to do something like this:
var innerDeferreds = [];
$.each(response, function (index,val) {
innerDeferreds.push(val.then(function (result){
results.push(result[0]);
}));;
});
$.when(innerDeferreds).then(function() { self.renderResult(results); });
Again, my guess is that you don't need the val.then in the first place, but I would need to look in a debugger to see what the values of response, index and val are. (If you set up a jsfiddle that would be super helpful!)

Multiple Promise Chains in Single Function

I have some code that will dynamically generate an AJAX request based off a scenario that I'm retrieving via an AJAX request to a server.
The idea is that:
A server provides a "Scenario" for me to generate an AJAX Request.
I generate an AJAX Request based off the Scenario.
I then repeat this process, over and over in a Loop.
I'm doing this with promises here: http://jsfiddle.net/3Lddzp9j/11/
However, I'm trying to edit the code above so I can handle an array of scenarios from the initial AJAX request.
IE:
{
"base": {
"frequency": "5000"
},
"endpoints": [
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/alvarengarichard",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
},
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/dkang",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
}
]
This seems like it would be straight forward, but the issue seems to be in the "waitForTimeout" function.
I'm unable to figure out how to run multiple promise chains. I have an array of promises in the "deferred" variable, but the chain only continues on the first one--despite being in a for loop.
Could anyone provide insight as to why this is? You can see where this is occuring here: http://jsfiddle.net/3Lddzp9j/10/
The main problems are that :
waitForTimeout isn't passing on all the instructions
even if waitForTimeout was fixed, then callApi isn't written to perform multiple ajax calls.
There's a number of other issues with the code.
you really need some data checking (and associated error handling) to ensure that expected components exist in the data.
mapToInstruction is an unnecessary step - you can map straight from data to ajax options - no need for an intermediate data transform.
waitForTimeout can be greatly simplified to a single promise, resolved by a single timeout.
synchronous functions in a promise chain don't need to return a promise - they can return a result or undefined.
Sticking with jQuery all through, you should end up with something like this :
var App = (function ($) {
// Gets the scenario from the API
// sugar for $.ajax with GET as method - NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario2');
};
var checkData = function (data) {
if(!data.endpoints || !data.endpoints.length) {
return $.Deferred().reject('no endpoints').promise();
}
data.base = data.base || {};
data.base.frequency = data.base.frequency || 1000;//default value
};
var waitForTimeout = function(data) {
return $.Deferred(function(dfrd) {
setTimeout(function() {
dfrd.resolve(data.endpoints);
}, data.base.frequency);
}).promise();
};
var callApi = function(endpoints) {
console.log('Calling API with given instructions ...');
return $.when.apply(null, endpoints.map(ep) {
return $.ajax({
type: ep.method,
dataType: ep.type,
url: ep.endpoint
}).then(null, function(jqXHR, textStatus, errorThrown) {
return textStatus;
});
}).then(function() {
//convert arguments to an array of results
return $.map(arguments, function(arg) {
return arg[0];
});
});
};
var handleResults = function(results) {
// results is an array of data values/objects returned by the ajax calls.
console.log("Handling data ...");
...
};
// The 'run' method
var run = function() {
getScenario()
.then(checkData)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(null, function(reason) {
console.error(reason);
})
.then(run);
};
return {
run : run
}
})(jQuery);
App.run();
This will stop on error but could be easily adapted to continue.
I'll try to answer your question using KrisKowal's q since I'm not very proficient with the promises generated by jQuery.
First of all I'm not sure whether you want to solve the array of promises in series or in parallel, in the solution proposed I resolved all of them in parallel :), to solve them in series I'd use Q's reduce
function getScenario() { ... }
function ajaxRequest(instruction) { ... }
function createPromisifiedInstruction(instruction) {
// delay with frequency, not sure why you want to do this :(
return Q.delay(instruction.frequency)
.then(function () {
return this.ajaxRequest(instruction);
});
}
function run() {
getScenario()
.then(function (data) {
var promises = [];
var instruction;
var i;
for (i = 0; i < data.endpoints.length; i += 1) {
instruction = {
method: data.endpoints[i].method,
type: data.endpoints[i].type,
endpoint: data.endpoints[i].endPoint,
frequency: data.base.frequency
};
promises.push(createPromisifiedInstruction(instruction));
}
// alternative Q.allSettled if all the promises don't need to
// be fulfilled (some of them might be rejected)
return Q.all(promises);
})
.then(function (instructionsResults) {
// instructions results is an array with the result of each
// promisified instruction
})
.then(run)
.done();
}
run();
Ok let me explain the solution above:
first of all assume that getScenario gets you the initial json you start with (actually returns a promise which is resolved with the json)
create the structure of each instruction
promisify each instruction, so that each one is actually a promise whose
resolution value will be the promise returned by ajaxRequest
ajaxRequest returns a promise whose resolution value is the result of the request, which also means that createPromisifiedInstruction resolution value will be the resolution value of ajaxRequest
Return a single promise with Q.all, what it actually does is fulfill itself when all the promises it was built with are resolved :), if one of them fails and you actually need to resolve the promise anyways use Q.allSettled
Do whatever you want with the resolution value of all the previous promises, note that instructionResults is an array holding the resolution value of each promise in the order they were declared
Reference: KrisKowal's Q
Try utilizing deferred.notify within setTimeout and Number(settings.frequency) * (1 + key) as setTimeout duration; msg at deferred.notify logged to console at deferred.progress callback , third function argument within .then following timeout
var App = (function ($) {
var getScenario = function () {
console.log("Getting scenario ...");
return $.get("http://demo3858327.mockable.io/scenario2");
};
var mapToInstruction = function (data) {
var res = $.map(data.endpoints, function(settings, key) {
return {
method:settings.method,
type:settings.type,
endpoint:settings.endPoint,
frequency:data.base.frequency
}
});
console.log("Instructions recieved:", res);
return res
};
var waitForTimeout = function(instruction) {
var res = $.when.apply(instruction,
$.map(instruction, function(settings, key) {
return new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.notify("Waiting for "
+ settings.frequency
+ " ms")
.resolve(settings);
}, Number(settings.frequency) * (1 + key));
}).promise()
})
)
.then(function() {
return this
}, function(err) {
console.log("error", err)
}
, function(msg) {
console.log("\r\n" + msg + "\r\nat " + $.now() + "\r\n")
});
return res
};
var callApi = function(instruction) {
console.log("Calling API with given instructions ..."
, instruction);
var res = $.when.apply(instruction,
$.map(instruction, function(request, key) {
return request.then(function(settings) {
return $.ajax({
type: settings.method,
dataType: settings.type,
url: settings.endpoint
});
})
})
)
.then(function(data) {
return $.map(arguments, function(response, key) {
return response[0]
})
})
return res
};
var handleResults = function(data) {
console.log("Handling data ..."
, JSON.stringify(data, null, 4));
return data
};
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
return {
// This will expose only the run method
// but will keep all other functions private
run : run
}
})($);
// ... And start the app
App.run();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
jsfiddle http://jsfiddle.net/3Lddzp9j/13/
You have a return statement in the loop in your waitForTimeout function. This means that the function is going to return after the first iteration of the loop, and that is where you are going wrong.
You're also using the deferred antipattern and are using promises in places where you don't need them. You don't need to return a promise from a then handler unless there's something to await.
The key is that you need to map each of your instructions to a promise. Array#map is perfect for this. And please use a proper promise library, not jQuery promises (edit but if you absolutely must use jQuery promises...):
var App = (function ($) {
// Gets the scenario from the API
// NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario');
};
// mapToInstructions is basically unnecessary. each instruction does
// not need its own timeout if they're all the same value, and you're not
// reshaping the original values in any significant way
// This wraps the setTimeout into a promise, again
// so we can chain it
var waitForTimeout = function(data) {
var d = $.Deferred();
setTimeout(function () {
d.resolve(data.endpoints);
}, data.base.frequency);
return d.promise();
};
var callApi = function(instruction) {
return $.ajax({
type: instruction.method,
dataType: instruction.type,
url: instruction.endPoint
});
};
// Final step: call the API from the
// provided instructions
var callApis = function(instructions) {
console.log(instructions);
console.log('Calling API with given instructions ...');
return $.when.apply($, instructions.map(callApi));
};
var handleResults = function() {
var data = Array.prototype.slice(arguments);
console.log("Handling data ...");
};
// The 'run' method
var run = function() {
getScenario()
.then(waitForTimeout)
.then(callApis)
.then(handleResults)
.then(run);
};
return {
run : run
}
})($);
App.run();

Replace synchronous AJAX logic with asynchronous logic [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I was having an obsolete JS library which was making API call Synchronous for which I decided to write JS function which can make them Async using jQuery.
In the following code the getData function is to be a generic function which makes API calls according to params passed and then extract data from the received XML/JS.
The second call(getAllData2) needs values from the result set of getData so I need a callback kind of thing in which the subsequent call can be made after the data is available from the 1st call.
Can this be achieved without the ajax success call back as I want getData function to remain generic.
I had tried jQuery promises but that gives me the raw data of the call instead of the processed one which I will have to process in each of the done callback separtely.
getData(param1,param2..){
var retData = {};
......Param dependent code here..
jQuery.ajax({
url:....,
.......
success: function(resp){
if(resp.length > 0){
jQuery.each(resp,function(key,val){
var i = 0;
var retObj = {};
jQuery.each(val,function(k,v){
retObj[k] = v;
i++;
});
retData[key] = retObj;
});
}
---Process recieved XML/JS and Insert values in retData here--
}
});
return retData;
}
var getAllData = getData(x,y);
var getAllData2 = getData(a,b); // this call needs param from getAllData.
Please suggest.
Thanks
Promises are indeed what you should be using.
That will allow you to structure your logic like this:
function processResult(resp) {
var retData = {};
if(resp.length > 0){
jQuery.each(resp,function(key,val){
var retObj = {};
jQuery.each(val,function(k,v){
retObj[k] = v;
});
retData[key] = retObj;
});
}
return retData;
}
getData(x, y)
.then(function (result) {
var processed = processResult(result);
return getData(processed);
})
.then(function (result) { // result is the result of the second getData()
// use result
});
If you want to do pre-processing of the results in your getData() function, again you can do this with promises:
function getData(param1,param2..) {
......Param dependent code here..
return $.ajax({
url:....,
.......
})
.then(function (resp) {
var retData = {};
if(resp.length > 0){
$.each(resp,function(key,val){
var retObj = {};
$.each(val,function(k,v){
retObj[k] = v;
});
retData[key] = retObj;
});
}
return retData;
});
}
getData(x, y)
.then(function (processedResult) {
return getData(processedResult, otherParameter);
})
.then(function (processedResult2) {
// use processedResult2
});

Structuring promises within angularjs

I have done a lot of reading around this, but ultimately the tutorials and guides I have found differ too much for me to get a decent grasp on this concept.
This is what I want to achieve:
1) Simple http request from our server [Any API for demonstration]
2) Run a function with data from (1). [Remove a property from the object]
3) Use result and length of (2) to run a loop of $http requests to our server. [Or any server]
4) This will result in 6 different objects. Run a function on these 6 objects. [Add a property]
5) Once ALL of this is done, run a separate function [Log "finished"]
How can this be achieved using promises? How do I pass data from (1) via a promise to (2)? Is this the right way to achieve what I need to do?
If anyone can show me how this should be structured it would be immensely helpful; I have kept the functions as simple as possible for this question.
Yes, promises are very nice to structure solutions for this kind of problems.
Simplified solution (more or less pseudo-code):
$http(...)
.then(function(response) {
// do something with response, for example:
var list = reponse.data.list;
// return it so that you can use it in the next 'then'.
return list;
})
.then(function(list) {
var promises = [];
angular.forEach(list, function(item) {
// perform a request for each item
var promise = $http(...).then(function(itemResponse) {
itemResponse.extraProperty = true;
return itemResponse;
});
// we make an array of promises
promises.push(promise);
});
// combine all promises into one and return it for the next then()
return $q.all(promises);
})
.then(function(itemsList) {
// itemsList is now an array of all parsed item responses.
console.log(itemsList);
});
(Hopefully this is right, I did not tested it.)
As you can see, you can return values in a callback to pass it to the next then(), or you can pass a promise, and this will result in calling the next callback when it resolves. $q.all() is used to combine multiple promises into one and resolve if all are resolved.
Edit: I realised that you can optionally leave out these three lines:
return list;
})
.then(function(list) {
But it is nice syntax though, because the separation of tasks is more visible.
Check code below, it could contains syntax error, the important is the structure. Step3 contains multiple(6) $http requests, it waits until the last request response to return a unique response object (array) containing response for each $http requets.
//Step 1
var Step1 = function () {
$http.get('api/controller').success(function (resp) {
var object1 = resp;
Step2(object1);
Step3(object1).then(function (resp) {
//resp.data is an array containing the response of each $http request
Step4(resp);
Step5();
});
});
}
//Step2
var Step2 = function(obj){
//do whatever with the object
}
//Step3
var Step3 = function (object1) {
var call = $q.defer();
var get1 = $http.get(object1[0].url);
var get2 = $http.get(object[1].url2);
//...
var get6 = $http.get(object[5].url6);
$q.all([get1, get2,..get6]).then(function (resp) {
call.resolve(resp);
});
return call.promise;
}
//Step4
var Step4 = function (resp) {
for (var i=0; i<resp.data.lenght;i++){
DoWhatEver(resp.data[i]);
};
}
//Step5
var Step5 = function () {
alert("Finished");
}
Step1(); //Call Step1 function
Don't know why you have difficulty implementing this, but maybe $q.all() is what you're missing:
var config1={method:'GET',url:'/api/...'};
$http(config1).success(function(resultsFrom1){
functionForResultsOf1(resultsFrom1);
})
var functionForResultsOf1 = function(resultsOf1){
//remove something from the result, assuming this is a synchronous operation
resultsOf1.splice()...;
var promises=makePromises(*pass whatever you want*);
$q.all(promises).then(function(aggregateOfAllCallsToServer){
angular.forEach(aggregateOfAllCallsToServer,function(data){
//do something to data from each call to the server
})
console.log("finished");
})
}
var makePromises = function(serverUrls){
var promises = [];
angular.forEach(serverUrls, function(url) {
var promise=$http({
url : '/api/'+url,
method: 'GET',
})
promises.push(promise);
});
return $q.all(promises);
}

Categories

Resources