Getting resolved data undefined in jquery - javascript

I am trying to call synchronous call for getting data count using ajax call.
Here is my Jquery Code:
var baseurl = _spPageContextInfo.webServerRelativeUrl;
console.log(baseurl);
var ItemCount = $.Deferred();
function tilesCount(tilename, count)
{
var url = baseurl + "/_api/web/lists/getByTitle('policies')/rootFolder/Folders?$expand=ListItemAllFields";
count = 0;
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
$(data.value).each(function (i, folder) {
count = count + 1;
});
console.log("Call 1: " + count)
ItemCount.resolve(count);
return count;
},
error: function(error){
console.log("Error: " + JSON.stringify(error));
ItemCount.reject;
}
});
}
$(document).ready(function () {
var count = tilesCount("");
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
});
Output:
Call 1: 1
Call 2: undefined
Synchronous call working perfectly, but I am getting data as undefined

Since ajax is asynchronous return count; will be empty
var count = tilesCount("");
So the best solution is to just passed a callback function inside your method which can be call whenever your ajax is completed
function tilesCount(tilename, count, callback)
Wrap this inside your callback function
function(count) {
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
}
so your $(document).ready will be like this and just add parameter count inside the callback
$(document).ready(function () {
tilesCount("", "", function(count) {
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
});
});
your javascript code would be like this now
var baseurl = _spPageContextInfo.webServerRelativeUrl;
console.log(baseurl);
var ItemCount = $.Deferred();
function tilesCount(tilename, count, callback)
{
var url = baseurl + "/_api/web/lists/getByTitle('policies')/rootFolder/Folders?$expand=ListItemAllFields";
count = 0;
$.ajax({
url: url,
dataType: 'json',
success: function(data) {
$(data.value).each(function (i, folder) {
count = count + 1;
});
console.log("Call 1: " + count)
ItemCount.resolve(count);
return callback(count);
},
error: function(error){
console.log("Error: " + JSON.stringify(error));
ItemCount.reject;
}
});
}
$(document).ready(function () {
tilesCount("", "", function(count) {
$.when(count).then(function(data){
console.log("Call 2: " + data);
});
});
});

Related

How to return a parent function when the function is recursive Javascript

I've been completing FreeCodeCamp and have given myself the task of fetching an image from the Wikipedia API. I am so close but I am just having trouble with this recursive function.
I'm having some trouble with an ajax request. I want the whole success function to return when obj===label. However, it is only returning one instance of findObjByLabel().
What can I do to make the success function completely return as soon as the label is found?
var wikiUrl = "https://en.wikipedia.org/w/api.php?action=query&format=json&titles=India&prop=pageimages&pithumbsize=300&callback=?";
// this retrieves info about the wikiUrlImg
$.ajax( {
url: wikiUrl,
data: {
format: 'json'
},
dataType: 'json',
type: 'GET',
headers: { 'Api-User-Agent': 'Example/1.0' },
success: function(data) {
console.log("wiki api success");
var findLabel = findObjByLabel(data,"India",1);
function findObjByLabel(obj, label, iterrations){
var itterationLimit = "9";
if (iterrations < itterationLimit){
for(var i in obj){
if(obj === label){
console.log(">>>>>>>>>>>>>>>>>>>>>>> !!!its the label!!! <<<<<<<<<<<<<<<<<<<<<<<<");
// ****************I want the success function to return here! ****************
return "something";
}else{
console.log(">>>>>>>>>>>>>>>>>>>>>>>its not the label<<<<<<<<<<<<<<<<<<<<<<<<");
console.log("i= " + i);
if(obj.hasOwnProperty(i)){
iterrations+=1;
console.log("obj[i] : " + obj[i]);
var foundLabel = findObjByLabel(obj[i], label, iterrations);
}
}
}
}
}//end of findObjByLabel function
}, //end of success
error: function(){
console.log("failure of getWiki api");
}
});
Substitute obj[i] for obj at if condition, use break within if statement, place return statement outside of for loop
var wikiUrl = "https://en.wikipedia.org/w/api.php?action=query&format=json&titles=India&prop=pageimages&pithumbsize=300&callback=?";
// this retrieves info about the wikiUrlImg
$.ajax({
url: wikiUrl,
data: {
format: 'json'
},
dataType: 'json',
type: 'GET',
headers: {
'Api-User-Agent': 'Example/1.0'
},
success: function(data) {
console.log("wiki api success");
var findLabel = findObjByLabel(data, "India", 1);
function findObjByLabel(obj, label, iterrations) {
var itterationLimit = "9";
if (iterrations < itterationLimit) {
for (var i in obj) {
if (obj[i] === label) {
console.log(">>>>>>>>>>>>>>>>>>>>>>> !!!its the label!!! <<<<<<<<<<<<<<<<<<<<<<<<");
// ****************I want the success function to return here! ****************
break; // break `for` loop
} else {
console.log(">>>>>>>>>>>>>>>>>>>>>>>its not the label<<<<<<<<<<<<<<<<<<<<<<<<");
console.log("i= " + i);
if (obj.hasOwnProperty(i)) {
iterrations += 1;
console.log("obj[i] : " + obj[i]);
var foundLabel = findObjByLabel(obj[i], label, iterrations);
}
}
}
}
return "something"; // return `"something"`
} //end of findObjByLabel function
console.log(findLabel); // "something"
}, //end of success
error: function() {
console.log("failure of getWiki api");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Creating an observable knockout array from a JSON response

C#/MVC/Knockout/JSON
I've got the following javascript:
function Feed(data) {
this.ID = ko.observable(data.ID);
this.RSSName = ko.observable(data.RSSName);
alert(data.RSSName + " " + data.ID);
}
function ViewModel() {
self = this;
self.CurrentFeeds = ko.observableArray([]);
self.isLoading = ko.observable(false);
self.StatusMessage = ko.observable("Loading");
$.ajax({
type: "GET",
url: '#Url.Action("RSSList", "RSS")',
success: function (data) {
var feeds = $.map(data, function (item) {
alert(item.RSSName + " " + item.ID + " 1");
return new Feed(item)
});
self.CurrentFeeds(feeds);
//self.CurrentFeeds(data);
},
error: function (err) {
alert(err.status + " : " + err.statusText);
}
});
self.save = function () {
self.deleteFeed = function (feed) {
};
};
}
The JSON response (as copied from fiddler) looks like this:
{"aaData":[{"ID":"0","RSSName":"Most Recent"},{"ID":"1","RSSName":"Website feed"}]}
Controller:
public JsonResult RSSList()
{
var query = (from t in db.tblRSSFeeds
select new ViewModels.RSSList()
{
ID = t.pkID.ToString(),
RSSName = t.szFeedName
}).OrderBy( t => t.RSSName).ToList();
var recent = new ViewModels.RSSList();
recent.ID = "0";
recent.RSSName = "Most Recent";
query.Insert(0, recent);
return Json( query, JsonRequestBehavior.AllowGet);
}
I'm thinking my issue has to do with the Feed(data) function in that it's only passing back one record. I tried setting the self.CurrentFeeds(data) as well with no luck. The "alerts" shown above show undefined but I can see the data coming down from fiddler...
For some reason the success function isn't seeing the data correctly to create the array. Why is this?
If it is the response:
{"aaData":[{"ID":"0","RSSName":"Most Recent"},{"ID":"1","RSSName":"Website feed"}]}
Change the success callback to:
$.ajax({
type: "GET",
url: '#Url.Action("RSSList", "RSS")',
success: function (data) {
var feeds = $.map(data.aaData, function (item) {
alert(item.RSSName + " " + item.ID + " 1");
return new Feed(item)
});
self.CurrentFeeds(feeds);
},
error: function (err) {
alert(err.status + " : " + err.statusText);
}
});
And i belive it works, because you are trying to map an object not an array, so you must to get the aaData that is the array to map.

JavaScript callback function when working withing a loop

This is what the code below does:
Goes to a table in a database and retrieves some search criteria I will send to Google API (the PHP file is getSearchSon.php)
After having the results, I want to loop around it, call the Google API (searchCriteriasFuc) and store the results in an array
The last part of the code is doing an update to two different tables with the results returned from Google API (updateSearchDb.php)
In my code, I am using setTimeout in a few occasions which I don't like. Instead of using setTimeout, I would like to properly use callback functions in a more efficient way (This might be the cause of my problem) What is the best way of me doing that?
$(document).ready(function() {
$.ajax({
url: 'getSearchSon.php',
type: 'POST',
async: true,
dataType: 'Text',
/*data: { }, */
error: function(a, b, c) { alert(a+b+c); }
}).done(function(data) {
if(data != "connection")
{
var dataSent = data.split("|");
var search_criterias = JSON.parse(dataSent[0]);
var date_length = dataSent[1];
var divison_factor = dataSent[2];
var length = search_criterias.length;
var arrXhr = [];
var totalResultsArr = [];
var helperFunc = function(arrayIndex)
{
return function()
{
var totalResults = 0;
if (arrXhr[arrayIndex].readyState === 4 && arrXhr[arrayIndex].status == 200)
{
totalResults = JSON.parse(arrXhr[arrayIndex].responseText).queries.nextPage[0].totalResults;
totalResultsArr.push(totalResults);
}
}
}
var searchCriteriasFuc = function getTotalResults(searchParam, callback)
{
var searchParamLength = searchParam.length;
var url = "";
for(var i=0;i<searchParamLength;i++)
{
url = "https://www.googleapis.com/customsearch/v1?q=" + searchParam[i] + "&cx=005894674626506192190:j1zrf-as6vg&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM&dateRestrict=" + date_length;
arrXhr[i] = new XMLHttpRequest();
arrXhr[i].open("GET", url, true);
arrXhr[i].send();
arrXhr[i].onreadystatechange = helperFunc(i);
}
setTimeout(function()
{
if (typeof callback == "function") callback.apply(totalResultsArr);
}, 4000);
return searchParam;
}
function callbackFunction()
{
var results_arr = this.sort();
var countResultsArr = JSON.stringify(results_arr);
$.ajax({
url: 'updateSearchDb.php',
type: 'POST',
async: true,
dataType: 'Text',
data: { 'countResultsArr': countResultsArr },
error: function(a, b, c) { alert(a+b+c); }
}).done(function(data) {
var resultsDiv = document.getElementById("search");
if(data == "NORECORD") resultsDiv.innerHTML = 'Updated failed. There was a problem with the database';
else resultsDiv.innerHTML = 'Update was successful';
}); //end second ajax call
}
//llamando funcion principal
var arrSearchCriterias = searchCriteriasFuc(search_criterias, callbackFunction);
}
else
{
alert("Problem with MySQL connection.");
}
}); // end ajax
});
How you did it in 2015
Callbacks are things of the past. Nowadays you represent result values of asynchronous tasks with Promises. Here is some untested code:
$(document).ready(function() {
$.ajax({
url: 'getSearchSon.php',
type: 'POST',
async: true,
dataType: 'text'
/*data: { }, */
}).then(function(data) {
if (data == 'connection') {
alert("Problem with MySQL connection.");
} else {
var dataSent = data.split("|");
var search_criterias = JSON.parse(dataSent[0]);
var date_length = dataSent[1];
var divison_factor = dataSent[2];
return Promise.all(search_criterias.map(function(criteria) {
return $.ajax({
url: "https://www.googleapis.com/customsearch/v1"
+ "?q=" + criteria
+ "&cx=005894674626506192190:j1zrf-as6vg"
+ "&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM"
+ "&dateRestrict=" + date_length,
type: 'GET'
});
})).then(function(totalResultsArr) {
totalResultsArr.sort();
var countResultsArr = JSON.stringify(totalResultsArr);
return $.ajax({
url: 'updateSearchDb.php',
type: 'POST',
async: true,
dataType: 'text',
data: { 'countResultsArr': countResultsArr },
error: function(a, b, c) { alert(a+b+c); }
});
}).then(function(data) {
var resultsDiv = document.getElementById("search");
if(data == "NORECORD") {
resultsDiv.innerHTML = 'Updated failed. There was a problem with the database';
} else {
resultsDiv.innerHTML = 'Update was successful';
}
});
}
}).then(null, function() {
alert('Some unexpected error occured: ' + e);
});
});
This is how you do it in 2016 (ES7)
You can just use async/await.
$(document).ready(async() => {
try {
var data = await $.ajax({
url: 'getSearchSon.php',
type: 'POST',
async: true,
dataType: 'text'
/*data: { }, */
});
if (data == 'connection') {
alert("Problem with MySQL connection.");
} else {
var dataSent = data.split("|");
var search_criterias = JSON.parse(dataSent[0]);
var date_length = dataSent[1];
var divison_factor = dataSent[2];
var totalResultsArr = await Promise.all(
search_criterias.map(criteria => $.ajax({
url: "https://www.googleapis.com/customsearch/v1"
+ "?q=" + criteria
+ "&cx=005894674626506192190:j1zrf-as6vg"
+ "&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM"
+ "&dateRestrict=" + date_length,
type: 'GET'
}))
);
totalResultsArr.sort();
var countResultsArr = JSON.stringify(totalResultsArr);
var data2 = await $.ajax({
url: 'updateSearchDb.php',
type: 'POST',
async: true,
dataType: 'text',
data: { 'countResultsArr': countResultsArr },
error: function(a, b, c) { alert(a+b+c); }
});
if(data2 == "NORECORD") {
resultsDiv.innerHTML = 'Updated failed. There was a problem with the database';
} else {
resultsDiv.innerHTML = 'Update was successful';
}
}
} catch(e) {
alert('Some unexpected error occured: ' + e);
}
});
UPDATE 2016
Unfortunately the async/await proposal didn't make it to the ES7 specification ultimately, so it is still non-standard.
You could reformat your getTotalResults function in the following matter, it would then search rather sequential, but it should also do the trick in returning your results with an extra callback.
'use strict';
function getTotalResults(searchParam, callback) {
var url = "https://www.googleapis.com/customsearch/v1?q={param}&cx=005894674626506192190:j1zrf-as6vg&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM&dateRestrict=" + (new Date()).getTime(),
i = 0,
len = searchParam.length,
results = [],
req, nextRequest = function() {
console.log('received results for "' + searchParam[i] + '"');
if (++i < len) {
completeRequest(url.replace('{param}', searchParam[i]), results, nextRequest);
} else {
callback(results);
}
};
completeRequest(url.replace('{param}', searchParam[0]), results, nextRequest);
}
function completeRequest(url, resultArr, completedCallback) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.onreadystatechange = function() {
if (this.readyState === 4 && this.status == 200) {
var totalResults = JSON.parse(this.responseText).queries.nextPage[0].totalResults;
resultArr.push(totalResults);
completedCallback();
}
};
req.send();
}
getTotalResults(['ford', 'volkswagen', 'citroen', 'renault', 'chrysler', 'dacia'], function(searchResults) {
console.log(searchResults.length + ' results found!', searchResults);
});
However, since you already use JQuery in your code, you could also construct all the requests, and then use the JQuery.when functionality, as explained in this question
Wait until all jQuery Ajax requests are done?
To get the callback execute after google calls are finished you could change:
var requestCounter = 0;
var helperFunc = function(arrayIndex)
{
return function()
{
if (arrXhr[arrayIndex].readyState === 4 && arrXhr[arrayIndex].status == 200)
{
requestCounter++;
totalResults = JSON.parse(arrXhr[arrayIndex].responseText).queries.nextPage[0].totalResults;
totalResultsArr.push(totalResults);
if (requestCounter === search_criterias.length) {
callbackFunction.apply(totalResultsArr);
}
}
}
}
then remove the setTimeout on searchCreteriaFuc.
Consider using promises and Promise.all to get all much cleaner :D

abort long polling ajax request and restart request with new parameter

how do I cancel an ajax request, then call it again with new parameter? with my code, the previous ajax requests still hang around.
var stats_url = '/stats';
var live_stats_ajax_object = $.ajax();
$(".check-round").on("click", function(){
live_stats_ajax_object.abort();
round = $(this).attr('class').split(' ')[0];
get_live_stats(round);
});
var get_live_stats = function(round) {
live_stats_ajax_object = $.ajax({
url: stats_url,
type: "GET",
data: 'live_stats=true' + "&event_id=" + $( "#event-list option:selected" ).val()
+ "&fight_id=" + $( 'input[name=fightlist]:checked' ).val()
+ "&round=" + round,
dataType: "json",
timeout: 3500,
complete: function(xhr, textStatus) {
console.log("polling again stats for " + round);
if (textStatus != "abort") {
setTimeout( function() { get_live_stats(round); }, 10000 );
}
},
success: function(data) {
console.log("polling and got live stats for " + round);
console.log(data);
}
})
.fail(function() {
console.log("polling failed and couldn't get live stats for " + round);
})
};
I have been on this for hours. Thanks
Edit, Updated
Try
// create empty object
var live_stats_ajax_object = {}
// reference for `setTimeout` within `get_live_stats`
, id = null
, stats_url = "/stats"
, get_live_stats = function (round) {
var eventlist = $( "#event-list option:selected" ).val()
, fightlist = $( 'input[name=fightlist]:checked' ).val();
live_stats_ajax_object = $.ajax({
type: "GET",
url: stats_url,
data: "live_stats=true&event_id="
+ eventlist
+ "&fight_id="
+ fightlist
+ "&round="
+ round,
timeout: 3500
});
// return `live_stats_ajax_object`
return live_stats_ajax_object
.done(function (data, textStatus, jqxhr) {
console.log("polling and got live stats for " + round + "\n"
, data);
})
.fail(function (jqxhr, textStatus, errorThrown) {
console.log("polling failed and couldn't get live stats for "
+ round);
})
.always(function (jqxhr, textStatus) {
if (textStatus !== "abort") {
// set `id` to `setTimeout`'s `numerical ID`
// call `get_live_stats()` within `setTimeout`
id = setTimeout(function () {
get_live_stats(round);
}, 10000);
}
});
};
$(".check-round").on("click", function () {
var round = $(this).attr('class').split(" ")[0];
// at initial click `live_stats_ajax_object` is empty object ,
// not having `jqxhr` `promise` property ;
// call `get_live_stats()` , which sets `live_stats_ajax_object`
// to `$.ajax()` , having `state` property at returned `jqxhr` object
if (!live_stats_ajax_object.hasOwnProperty("state")) {
get_live_stats(round);
} else {
// if `id` was set during initial call to `get_live_stats()`
if (id) {
// `.abort()` previous `live_stats_ajax_object` request ,
// `clearTimeout()` of `id` , set `id` to `null`
// call `get_live_stats()` with current `round` argument
live_stats_ajax_object.abort();
clearTimeout(id);
id = null;
get_live_stats(round);
}
}
});
jsfiddle http://jsfiddle.net/guest271314/7wrdo5wr/

Javascript/JQuery: Reload a page AFTER two functions have completed

I have a save button that calls a function to open a modal dialog with two buttons; "Save Timelines" and "Cancel". The "Save Timeline" button calls two functions AFTER WHICH the page needs to reload. I've tried a few different ways to get this done...
1 Just calling the functions pragmatically:
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
editSavedTimelines();
saveTimelines();
$(this).dialog("close");
location.reload();
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
2 Setting up a callback:
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
editSavedTimelines(saveTimelines());
location.reload();
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
3 Using JQuery $.when().do():
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
$.when(editSavedTimelines(), saveTimelines()).do(location.reload());
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
My issue, on all three attempts, comes about when the "Save Timelines" button is clicked... the page reloads and none of the functions are run. When I pull the location.reload() call out of each example, the functions run as I want them.
Is there a way to reload the page ONLY AFTER the functions are completed?
For reference, here are the functions I'm calling:
function saveTimelines() {
console.log("start save");
for (i=1; i < timelineIndex + 1; i++) {
var dow = startdow;
var clientValue = $("#clientNameSelect" + i).val();
var projectValue = $("#projectSelect" + i).val();
var taskValue = $("#taskSelect" + i).val();
var billingValue = $("#billingSelect" + i).val();
var activityValue = $("#activitySelect" + i).val();
var stateValue = $("#states" + i).val();
var sundayValue = $("#sun" + i).val();
var mondayValue = $("#mon" + i).val();
var tuesdayValue = $("#tue" + i).val();
var wednesdayValue = $("#wed" + i).val();
var thursdayValue = $("#thu" + i).val();
var fridayValue = $("#fri" + i).val();
var saturdayValue = $("#sat" + i).val();
$.ajax({
type: "GET",
url:"biqqyzqyr?act=API_DoQuery&query={'6'.EX.'" + projectValue + "'}AND{'16'.TV.'" + currUserEmail + "'}&clist=3&includeRids=1&fmt=structured",
dataType: "xml",
success: function (xml) {
$(xml).find("record").each(function () {
var resourceMap = new Array();
$(this).children().each(function () {
var name = $(this).attr("id");
var value = $(this).text();
resourceMap[name] = value;
});
resourceRecords.push(resourceMap);
});
console.log("hi");
var resourceRId = '3';
for (var j = 0; j < resourceRecords.length; j++) {
resourceOptions = resourceRecords[j][resourceRId];
console.log(resourceOptions);
}
$.ajax({
type: "GET",
url: "biha4iayz?act=API_AddRecord&_fid_12=" + dow + "&_fid_36=" + clientValue + "&_fid_9=" + projectValue + "&_fid_7=" + taskValue + "&_fid_10=" + billingValue + "&_fid_15=" + activityValue + "&_fid_11=" + stateValue + "&_fid_13=" + sundayValue + "&_fid_57=" + mondayValue + "&_fid_58=" + tuesdayValue + "&_fid_59=" + wednesdayValue + "&_fid_60=" + thursdayValue + "&_fid_61=" + fridayValue + "&_fid_62=" + saturdayValue + "&_fid_17=" + resourceOptions,
dataType: "xml",
success: function () {
console.log(i+ "new")
},
fail: loadFail
});
},
fail: loadFail
});
}
alert(timelineIndex+savedTimelineIndex+" timelines have been saved to the system...");
}
function editSavedTimelines(callback) {
console.log("start edit");
for (j=1; j < savedTimelineIndex + 1; j++) {
var dow = startdow;
var savedRId = $("#recordsaved" + j).val();
var sundayValue = $("#sunsaved" + j).val();
var mondayValue = $("#monsaved" + j).val();
var tuesdayValue = $("#tuesaved" + j).val();
var wednesdayValue = $("#wedsaved" + j).val();
var thursdayValue = $("#thusaved" + j).val();
var fridayValue = $("#frisaved" + j).val();
var saturdayValue = $("#satsaved" + j).val();
console.log(savedRId);
$.ajax({
type: "GET",
url: "biha4iayz?act=API_EditRecord&rid=" + savedRId + "&_fid_13=" + sundayValue + "&_fid_57=" + mondayValue + "&_fid_58=" + tuesdayValue + "&_fid_59=" + wednesdayValue + "&_fid_60=" + thursdayValue + "&_fid_61=" + fridayValue + "&_fid_62=" + saturdayValue,
dataType: "xml",
success: function () {
},
fail: loadFail
});
}
}
The problem with your usage of when is that both of your functions don't return anything. Your call essentially amounts to this:
$.when(undefined, undefined)
Your saveTimelines function is the more complicated function because you make a 2nd ajax call in the callback of the first. And to make matters even worse, these ajax calls are in a loop. So your function isn't "complete" until the inner ajax calls for each iteration of the loop are complete.
I would strongly suggest trying to completely redesign this to simplify things. If you can eliminate the loop as well as the nested ajax calls, this would be much easier.
That being said, lets look at how we could overcome this issue. First to deal with the issue of the inner ajax call, you can solve this by creating your own deferred object. Something like this (ignoring the loop for the moment):
function saveOneTimeline(/* any params here, such as i */) {
// create a deferred object which will be returned by this function and resolved once all calls are complete
var def = $.Deferred();
/* ... */
$.ajax({
/* ... */
success: function (xml) {
/* ... */
$.ajax({
/* ... */
success: function () {
// we are done, resolve the deferred object
def.resolve();
}
});
}
});
// return the deferred object so that the calling code can attach callbacks/use when
return def;
}
Then finally, our previous method can be called in a loop, placing the returned deferred objects into an array and then using when to return a promise that will only resolve once all of the deferreds resolve. It would look something like this:
function saveTimelines() {
// an array to store all of the deferreds
var defs = [];
for (i=1; i < timelineIndex + 1; i++) {
defs.push(saveOneTimeline(i));
}
// call when on the array of deferred objects and return the resulting promise object
return $.when.apply($, defs);
}
Your editSavedTimelines is slightly less complex due to the fact that you don't have nested ajax calls. However, you still have a loop. A very similar approach can be used, except the helper function can simply return the object returned by the ajax call directly.
As you can see, this all very complex. It would probably be a much better idea to instead try to eliminate some of your complexity to avoid having to go to these lengths. Perhaps if you can make one bulk ajax call instead of many in a loop then allow the backend code to handle the separation.
This should work for you. It uses an array of Deferred objects for the for ajax calls in for loops, and has one final deferred object for when all of those are complete. The outer function listens for completion of those deferred objects. Note that I have snipped a lot of code that isn't relevant, and changed from success callbacks to .done() for your ajax calls.
function genSaveTimelinesModal() {
$("#saveTimelineDialog").dialog({
resizable: false,
height: 250,
modal: true,
buttons: {
"Save timelines": function() {
$.when(editSavedTimelines(), saveTimelines()).done(function() {
location.reload();
});
},
Cancel: function() {
$(this).dialog("close");
}
}
});
}
function saveTimelines() {
var finalDef = $.Deferred();
var defs = [];
for (i=1; i < timelineIndex + 1; i++) {
var def = $.Deferred();
defs.push(def);
$.ajax({...}).done(function(xml) {
$.ajax({...}).done(function() {
def.resolve(true);
});
});
}
$.when.apply(null, defs).done(function() {
finalDef.resolve();
});
return finalDef.promise();
}
function editSavedTimelines() {
var finalDef = $.Deferred();
var defs = [];
for (j=1; j < savedTimelineIndex + 1; j++) {
var def = $.Deferred();
defs.push(def);
$.ajax({...}).done(function() {
def.resolve(true);
});
}
$.when.apply(null, defs).done(function() {
finalDef.resolve(true);
});
return finalDef.promise();
}

Categories