For loop not executing http service correctly - javascript

I am trying to call an http service within a for loop through a function (callAPI).
Here is my code. The problem I am having with this code is that I don't get the correct id called in the right order when the http service catches up. On the callAPI function it goes through all the calls of console.log(id) before even running one http service async request then it runs the http service calls but with the wrong order of id's..For instance I get back an array of [6,6,3] and it runs it in the order of [3,6,6]. Any help is appreciated.
for (var i = 0; i < idArray.length; i++) {
callAPI(idArray[i]);
}
function getItems() {
return self.choices;
}
function callAPI(id) {
console.log(id);
$http({
method: 'GET',
url: '/api/organizations/' + orgID + '/' + id + '/meal'
}).success(function (data) {
console.log(id);
console.log(data);
// idLocal = id;
angular.forEach(data, function(value,key) {
angular.forEach(value, function(value,key) {
angular.forEach(value, function(value,key) {
if (key == 'description') {
items.push(value);
}
if (key == 'drop_name') {
self.dropName = value;
}
});
});
});
self.choices.push({
id: id,
choice: items,
dropname: self.dropName
});
items = [];
getItems();
}).error(function (data) {
console.log('failed');
});
}

I prefer to do something like this but there are multiple other solutions:
//set index
var index = 0;
function callAPI(id) {
//increment index
index++
$http({
method: 'GET',
url: '/api/organizations/' + orgID + '/' + id + '/meal'
}).success(function (data) {
//call function again with next index if exists
if (index <= idArray.length) {
callAPI(idArray[index])
}
}).error(function (data) {
console.log('failed');
});
}
//call function with 0 index
callApi(idArray[index])
This will run one async request, when that is returned then run the next. You can of course handle the return data any way you want. Also, as mentioned using the promise library included with angular "q" is another good option.

Related

looped nested $http js functions

I have two javscript async $http funtions: which I am using with angular js in a nested way to create a table dynamically.
I want some way to execute these functions to synchronously.
Right now, j loop executes only on the initial value of resultB. Once the table is compiled then the fuctB gets executed for all values of i.
$scope.funcA = function() {
$http({
method : 'GET',
url : url,
}).then(function successCallback(response) {
$scope.resultA = response.data;
//process based on $scope.resultA
for (var i = 0; i < $scope.resultA .length; i++){
$scope.funcB($scope.resultA[i][0]);
for(j=0; j<$scope.resultB .length; j++){
//process based on $scope.resultB
}
}
$compile(/* document element*/);
}, function errorCallback(response) {
console.log(response.statusText);
});
}
$scope.funcB = function(k){
$http({
method : 'GET',
url : url+k
data: k ,
}).then(function successCallback(response) {
return $scope.resultB = response.data;
}, function errorCallback(response) {
console.log(response.statusText);
});
}
$scope.funcB = function(i) {
// return promise
return $http({....
data: i ;
$scope.resultB = response.data;
};
}
$http{.....
$scope.resultA =response.data;
for(var i =0; i< $scope.resultA.length; i++{
process based on i value
$scope.funcB(i).then(function() {
// execute this part after promise completed (request B has ended and returned result)
for(var j =0; j<$scope.resultB.length;j++{
process based on i and j;
}
}
compile(the document element);
});
}
Please check some tutorial on Promises it will help you understand what's going on here, e.g. http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/ but there are plenty of these on internet...

Collect AJAX results in order

I have an array of values (myarray), that I want to iterate through and run an AJAX request on each iteration. I've put each ajax request inside another array (requests), so that I can call an alert when all AJAX requests have completed:
Like so:
var requests = [];
for (i = 0; i < myarray.length; ++i) {
requests.push($.ajax({
url: 'anotherurl?=' + myarray[i],
dataType: "JSONP",
success: function (data) {
array_of_results.push(data);
}
}));
}
$.when.apply($, requests).done(function() {
alert('complete');
});
All the results are collected in array_of_results. However due to the AJAX requests taking different lengths of time to complete, this array doesn't have the results in the original order.
Is there any way to order this array?
I hope I've made sense. I appreciate this is quite convoluted.
Have you tried the following? I think this should work. All the responses should be available, in order, in the success function of the when().
var requests = [];
for (i = 0; i < myarray.length; ++i) {
requests.push($.ajax({
url: 'anotherurl?=' + myarray[i],
dataType: "JSONP"
}));
}
$.when.apply($, requests).done(function() {
array_of_results = arguments;
alert('complete');
});
Instead of using a loop consider using recursion. Here's a complete example:
var myArray = [
"somevalue",
"some other value",
"another value"
];
var i = myArray.length;
var responses = [];
function doRequests(){
i--;
$.ajax({
url: "myurl.php",
data: {paramname: myArray[i]}
}).done(function(response){
responses.push(response);
if(i>0) doRequests();
else{
// all requests sent.. do stuff
// responses array is in order
console.log(responses);
alert("all done!");
}
});
}
You can add a custom attribute to your $.ajax object, setted to your i var.
var requests = [];
for (i = 0; i < myarray.length; ++i) {
requests.push($.ajax({
url: 'anotherurl?=' + myarray[i],
dataType: "JSONP",
myCustomIndex: i,
success: function (data) {
array_of_results[this.myCustomIndex] = data;
}
}));
}
$.when.apply($, requests).done(function() {
alert('complete');
});
JavaScript is really permisive, if you attribute a value to an array out of its bounds (higher than 0), the size of the array will be automaticaly set to the right amount.
How about using jquery.ajax call with async setting as false. This way the reaponse will be in order as requested...
Building on #Christo's answer - using arrays map function
var array_of_results = [];
var requests = myarray.map(function(item, index) {
return $.ajax({
url: 'anotherurl?=' + item,
dataType: "JSONP",
success: function (data) {
array_of_results[index] = data;
}
}
});
...

Ajax calls inside loop need sequential responses

I need to make 3 or less ajax calls, and the responses need to be appended to the dom in the same order they were requested.
I have the following function, but the problem is that the responses that I get are not necessarily in the correct order when they get appended to the dom.
I wouldn't want to use the async: false property because it blocks the UI and it's a performance hit of course.
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
});
}
};
You need to append the article to a certain position, based on for example the i variable you have. Or you could wait for all of the requests and then append them in order. Something like this:
mod.getArticles = function( ){
var load = function( id ) {
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
});
};
var onDone = function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
};
var requests = [];
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
requests.push(load(mod.vars.pushIds[i]));
}
$.when.apply(this, requests).done(function() {
var results = requests.length > 1 ? arguments : [arguments];
for( var i = 0; i < results.length; i++ ){
onDone(results[i][0]);
}
});
};
Here is an example using i to append them in the proper order when they all finish loading:
mod.getArticles = function( ){
// initialize an empty array of proper size
var articles = Array(3 - mod.vars.ajaxCount);
var completed = 0;
//mod.vars.ajaxCount could start at 0-2
for( var i = mod.vars.ajaxCount; i < 3; i++ ){
// prevent i from being 3 inside of done callback
(function (i){
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[i];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
completed++;
if (data.length) {
// store to array in proper index
articles[i - mod.vars.ajaxCount] = data;
} else {
console.error('get article ajax output error');
}
// if all are completed, push in proper order
if (completed == 3 - mod.vars.ajaxCount) {
// iterate through articles
for (var j = mod.vars.ajaxCount; j < 3; j++) {
// check if article loaded properly
if (articles[j - mod.vars.ajaxCount]) {
mod.appendArticle(articles[j - mod.vars.ajaxCount]);
}
}
}
});
}(i));
}
};
var success1 = $.ajax...
var success2 = $.ajax...
var success3 = $.ajax...
$.when(success1, success2, success3).apply(ans1, ans2, ans3) {
finalDOM = ans1[0]+ans2[0]+ans3[0];
}
Check this for more reference. This is still async, but it waits for all of them to complete. You know the order of invocation already, as its done through your code, so add the dom elements accordingly.
Solutions that rely solely on closures will work up to a point. They will consistently append the articles of a single mod.getArticles() call in the correct order. But consider a second call before the first is fully satisfied. Due to asynchronism of the process, the second call's set of articles could conceivably be appended before the first.
A better solution would guarantee that even a rapid fire sequence of mod.getArticles() calls would :
append each call's articles in the right order
append all sets of articles in the right order
One approach to this is, for each article :
synchronously append a container (a div) to the DOM and keep a reference to it
asynchronously populate the container with content when it arrives.
To achieve this, you will need to modify mod.appendArticle() to accept a second parameter - a reference to a container element.
mod.appendArticle = function(data, $container) {
...
};
For convenience, you may also choose to create a new method, mod.appendArticleContainer(), which creates a div, appends it to the DOM and returns a reference to it.
mod.appendArticleContainer = function() {
//put a container somewhere in the DOM, and return a reference to it.
return $("<div/>").appendTo("wherever");
};
Now, mod.getArticles() is still very simple :
mod.getArticles = function() {
//Here, .slice() returns a new array containing the required portion of `mod.vars.pushIds`.
//This allows `$.map()` to be used instead of a more cumbersome `for` loop.
var promises = $.map(mod.vars.pushIds.slice(mod.vars.ajaxCount, 3), function(id) {
var $container = mod.appendArticleContainer();//<<< synchronous creation of a container
return $.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML'
}).then(function(data) {
if (data.length) {
mod.appendArticle(data, $container);//<<< asynchronous insertion of content
} else {
return $.Deferred().reject(new Error("get article ajax output error"));
}
}).then(null, function(e) {
$container.remove();//container will never be filled, so can be removed.
console.error(e);
return $.when(); // mark promise as "handled"
});
});
return $.when.apply(null, promises);
};
mod.getArticles() now returns a promise of completion to its caller, allowing further chaining if necessary.
Try utilizing items within mod.vars array as indexes; to set as id property of $.ajaxSettings , set returned data at this.id index within an array of responses. results array should be in same order as mod.vars values when all requests completed.
var mod = {
"vars": [0, 1, 2]
};
mod.getArticles = function () {
var results = [];
var ids = this.vars;
var request = function request(id) {
return $.ajax({
type: "GET",
url: "/ajax/article/" + id + "/",
// set `id` at `$.ajaxSettings` ,
// available at returned `jqxhr` object
id: id
})
.then(function (data, textStatus, jqxhr) {
// insert response `data` at `id` index within `results` array
console.log(data); // `data` returned unordered
// set `data` at `id` index within `results
results[this.id] = data;
return results[this.id]
}, function (jqxhr, textStatus, errorThrown) {
console.log("get article ajax error", errorThrown);
return jqxhr
});
};
return $.when.apply(this, $.map(ids, function (id) {
return request(id)
}))
.then(function () {
$.map(arguments, function (value, key) {
if (value.length) {
// append `value`:`data` returned by `$.ajax()`,
// in order set by `mod.vars` items:`id` item at `request`
mod.appendArticle(value);
} else {
console.error("get article ajax output error");
};
})
});
};
mod.getArticles();
jsfiddle http://jsfiddle.net/6j7vempp/2/
Instead of using a for loop. Call your function in response part of previous function.
//create a global variable
var counter = 0;
function yourFunc(){
mod.getArticles = function( ){
//mod.vars.ajaxCount could start at 0-2
//mod.vars.pushIds is an array with the ids to be ajaxed in
var id = mod.vars.pushIds[counter ];
$.ajax({
url: '/ajax/article/' + id + '/',
type: "GET",
dataType: 'HTML',
error: function() {
console.error('get article ajax error');
}
}).done( function( data ) {
if (data.length) {
mod.appendArticle( data );
} else {
console.error('get article ajax output error');
}
//increment & check your loop condition here, so that your responses will be appended in same order
counter++;
if (counter < 3)
{ yourFunc(); }
});
};
}
I'm faced same problem i'm solve this problem using following way.
just use async for get sequence wise response
<script type="text/javascript">
var ajax1 = $.ajax({
async: false,
url: 'url',
type: 'POST',
data: {'Data'},
})
.done(function(response) {
console.log(response);
});

Sending a variable with ajax call .done

Is there a possible way to send the variable, 'schedule_id[i]', with the result of the call. Also is it possible to add this variable in the data object?
My code:
for (var i = 0; i < schedule_id.length; i++) {
//Ajax call
$.ajax({
url: "http://api.viewer.zmags.com/schedules/" + schedule_id[i] + "?key=" + api_key
})
//
.done(function(data){
}
So you need asynchronies ajax call in synchronies manner right?
So you need to create one separate function which is call ajax request and return the result and use in subsequent request.
Like:-
for (var i = 0; i < schedule_id.length; i++) {
var result;
if (i == 0)
result = callfunction(0,schedule_id[i]);
else
result = callfunction(result,schedule_id[i]);
}
function callfunction(passresult,schedule_id) {
$.ajax({
url: "http://api.viewer.zmags.com/schedules/" + schedule_id + "?key=" + api_key
})
.done(function (data) {
return data;
});
}
construct the ajax call like this:
$.ajax({
url: 'http://api.viewer.zmags.com/schedules/',
type: 'POST' // or GET,
data: {
schedule_ids: schedule_id, //array
key: api_key
},
success: function (data) {
//success callback
}
});

Uncaught ReferenceError: error is not defined in Ajax Callback

In my App, I get a list of Events from a Sharepoint Calendar List. That part works perfectly.
However after I get the collection of results, for each item I need to get the Display Form Url, which is another REST Call with the ListItem ID.
However I get the error below, but I still dont know what the problem might be
Uncaught ReferenceError: error is not defined App.js:87(anonymous function) App.js:87$.ajax.error App.js:40c jquery-1.9.1.min.js:22p.fireWith jquery-1.9.1.min.js:22k jquery-1.9.1.min.js:24send.r
I based my code on this answer:
https://sharepoint.stackexchange.com/questions/119236/how-to-get-the-display-form-url-using-rest
My adapted code is like this:
var SPHostUrl;
var SPAppWebUrl;
var ready = false;
// this function is executed when the page has finished loading. It performs two tasks:
// 1. It extracts the parameters from the url
// 2. It loads the request executor script from the host web
$(document).ready(function () {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var param = params[i].split("=");
switch (param[0]) {
case "SPAppWebUrl":
SPAppWebUrl = decodeURIComponent(param[1]);
break;
case "SPHostUrl":
SPHostUrl = decodeURIComponent(param[1]);
break;
}
}
// load the executor script, once completed set the ready variable to true so that
// we can easily identify if the script has been loaded
$.getScript(SPHostUrl + "/_Layouts/15/SP.RequestExecutor.js", function (data) {
ready = true;
getItems();
});
});
function getListItemFormUrl(webUrl, listName, listItemId, formTypeId, complete, failure) {
$.ajax({
url: webUrl + "/_api/web/lists/GetByTitle('" + listName + "')/Forms?$select=ServerRelativeUrl&$filter=FormType eq " + formTypeId,
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
var url = data.d.results[0].ServerRelativeUrl + '?ID=' + listItemId
complete(url);
},
error: function (data) {
failure(data);
}
});
}
// this function retrieves the items within a list which is contained within the parent web
function getItems() {
// only execute this function if the script has been loaded
if (ready) {
// the name of the list to interact with
var listName = "Events";
// the url to use for the REST call.
var url = SPAppWebUrl + "/_api/SP.AppContextSite(#target)" +
// this is the location of the item in the parent web. This is the line
// you would need to change to add filters, query the site etc
// "/web/lists/getbytitle('" + listName + "')/items?" +
"/web/lists/getbytitle('" + listName + "')/items?$select=Title,Category,EventDate,Description,EncodedAbsUrl,ID" +
"&#target='" + SPHostUrl + "'";
// create new executor passing it the url created previously
var executor = new SP.RequestExecutor(SPAppWebUrl);
// execute the request, this is similar although not the same as a standard AJAX request
executor.executeAsync(
{
url: url,
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
// parse the results into an object that you can use within javascript
var results = JSON.parse(data.body);
var events = [];
$.each(results.d.results, function (i, obj) {
//Usage
getListItemFormUrl(SPAppWebUrl, 'Calendar', obj.ID, 4,
function (url) {
console.log('Display from url for list item: ' + url);
},
function (sender, args) {
console.log(JSON.stringify(error));
})
//use obj.id and obj.name here, for example:
var event = {
date: Date.parse(obj.EventDate).toString(),
type: obj.Category,
title: obj.Title,
description: obj.Description,
url: obj.EncodedAbsUrl + 'DispForm.aspx?ID=' + obj.ID
}
events.push(event);
});
var myJsonString = JSON.stringify(events);
$("#eventCalendarInline").eventCalendar({
jsonData: events,
openEventInNewWindow: true,
showDescription: true,
txt_GoToEventUrl: "Go to event"
});
Communica.Part.init();
},
error: function (data) {
// an error occured, the details can be found in the data object.
alert("Ooops an error occured");
}
});
}
}
Under //Usage:
function (sender, args) {
console.log(JSON.stringify(error));
})
error does not seem to be defined

Categories