I am trying to use jQuery's $.when.apply to wait for an unknown number of requests to finish before calling the next function (loadTab). I thought I was using it correctly, but it is definitely not waiting for all the requests to finish before loadTab() is called, so I'm not sure what is wrong. Here is my code:
function update(changes) {
var deferreds = [];
// must loop over changes and call individual requests
for(var i = 0; i < changes.length; i++) {
var direction = changes[i]['direction'];
var data = changes[i]['data'];
if(direction == 'add') {
deferreds.push[add(data)];
}
else if(direction == 'edit') {
deferreds.push[edit(data)];
}
else if(direction == 'delete') {
deferreds.push[delete(data)];
}
}
return $.when.apply($, deferreds); // this when is resolving too soon
}
function add(data) {
return $.ajax({
url: 'add',
data: data,
method: 'post',
error: ajaxErrorFcn
})
.then(function(response) {
handleTimeout(response);
});
}
function edit(data) {
return $.ajax({
url: 'edit',
data: data,
method: 'post',
error: ajaxErrorFcn
})
.then(function(response) {
handleTimeout(response);
});
}
function delete(data) {
return $.ajax({
url: 'delete',
data: data,
method: 'post',
error: ajaxErrorFcn
})
.then(function(response) {
handleTimeout(response);
});
}
// this is the sequence of events I'm trying to sort out
showLoad('#container');
var changes = buildChangesArray();
update(changes)
.then(function(response) {
if(handleTimeout(response)) {
// this is executing before the requests triggered by update() are complete
return loadTab()
.then(function(response) {
// do some other stuff
});
}
})
.then(function(response) {
hideLoad('#container');
});
Update:
The original issue was resolved (There was a typo in my calls to .push(), used brackets instead of parentheses), but now I have a new issue with this code. I need to modify the update() function to run the delete actions first, and then run the add and edit actions. This is what I have, but now I am seeing the add and edit actions start to run before the delete actions finish:
function update(changes) {
var deferreds = [];
var deletes = [];
// must loop over changes and call individual requests
for(var i = 0; i < changes.length; i++) {
var direction = changes[i]['direction'];
var data = changes[i]['data'];
if(direction == 'add') {
deferreds.push(add(data));
}
else if(direction == 'edit') {
deferreds.push(edit(data));
}
else if(direction == 'delete') {
deletes.push(delete(data));
}
}
// we need to perform all the delete operations first, then the adds/edits
return $.when.apply($, deletes) // this when is resolving too soon
.then(function(response) {
return $.when.apply($, deferreds);
});
}
Well, looks like I found my own solution :)
I think the problem was that when pushing the asynchronous calls to add(), edit(), and delete() into the arrays, they are also getting called at that point! This wasn't a problem in the first version because then it didn't matter what order the adds, edits, and deletes were performed in as long as they were all done before loadTab() was called. However, it does pose a problem if all the deletes need to be called before calling any adds or edits, because any adds or edits found in the array before the deletes will start running as soon as they are pushed to the array rather than waiting for the deletes.
To fix this, I changed the code to the following:
function update(changes) {
var loop = function(deleteOnly) {
var array = [];
// must loop over changes and call individual requests
for(var i = 0; i < changes.length; i++) {
var direction = changes[i]['direction'];
var data = changes[i]['data'];
if(direction == 'add' && !deleteOnly) {
array.push(add(data));
}
else if(direction == 'edit' && !deleteOnly) {
array.push(edit(data));
}
else if(direction == 'delete' && deleteOnly) {
array.push(delete(data));
}
}
return array;
};
// we need to perform all the delete operations first
return $.when.apply($, loop(true)) // true means only get the deletes
.then(function(response) {
return $.when.apply($, loop(false)); // false means only get the adds/edits
});
}
So the requests are still starting running as soon as they are pushed into the array, but this way we can separate the deletes to makes sure they finish first.
Related
i want to make a review within my code, i am making a JavaScript/ajax code that will run every sec. Now here is what my code does,
It will check if there are changes in the queue, if there is changes, then it will alert, if there were no changes, then it will continue running to always check, now why is it sometimes it will alert and sometimes it will not alert. Here is my code:
function check_getqueue(clinicID, userID) {
var tmpCountQ = [];
$.ajax({
url: siteurl+"sec_myclinic/checkingUpdates/"+clinicID+"/"+userID,
type: "POST",
dataType: "JSON",
success: function(data) {
for(var i=0;i<data.length;i++) {
tmpCountQ.push(data[i]['queue_id']);
};
if(typeof lastCon[0] != "undefined")
{
for(j=0;j < tmpCountQ.length;j++)
{
if(tmpCountQ[j] != lastCon[j])
{
$("#boxqueue").empty();
alert("there is change");
// refresh_afterdel(clinicID, userID);
lastCon[j] = tmpCountQ[j];
}
}
}
else
{
lastCon = tmpCountQ;
}
// console.log("lastCon "+lastCon)
// console.log("tmpCountQ "+tmpCountQ);
}
});
}
variable j is not initialised. So it may be updating the same global j
function check_getqueue(clinicID, userID) {
var tmpCountQ = [];
$.ajax({
url: siteurl+"sec_myclinic/checkingUpdates/"+clinicID+"/"+userID,
type: "POST",
dataType: "JSON",
success: function(data) {
for(var i=0;i<data.length;i++) {
tmpCountQ.push(data[i]['queue_id']);
};
if(typeof lastCon[0] != "undefined")
{
for(var j=0;j < tmpCountQ.length;j++)
{
if(tmpCountQ[j] != lastCon[j])
{
$("#boxqueue").empty();
alert("there is change");
// refresh_afterdel(clinicID, userID);
lastCon[j] = tmpCountQ[j];
}
}
}
else
{
lastCon = tmpCountQ;
}
// console.log("lastCon "+lastCon)
// console.log("tmpCountQ "+tmpCountQ);
}
});
}
You are not guaranteed synchronous calls to the server and all variables not declared in the success or error functions as local vars are therefore not guaranteed to be fifo which is what a queue demands. The solution is to use a buffer with package sequence numbers to organize the data into a fifo queue. This means changing what is returned into a JSON array of JSON objects with an integer id. When checking the data make sure you have seen the correct packets before alerting. When you have the next packet alert and repeat until the server's queue is empty.
I have some javascript code that updates some data to a database using a http handler, but this async call is made inside an .each loop. At the end of the loop I make a call to function CancelChanges() that refreshed the page. The problem is that the page seems to refresh before the database is updated. The .each loop seems to finish after the call to CancelChanges(). How can I make sure the page is refreshed after all the async calls are completed in the .each loop?
function SaveChanges() {
if (PreSaveValidation()) {
var allChangesSucceeded = true;
var studioId = $("#param_studio_id").val();
var baseDate = $("#param_selected_month").val().substring(6, 10) + $("#param_selected_month").val().substring(0,2);
var currency = "CAD";
var vacationPct = null;
var gvAdmissible = null;
$(".editable-unsaved").each( function() {
var newSalary = $(this).text();
var disciplineId = $(this).data("disciplineid");
var seniorityId = $(this).data("seniorityid");
var handlerCommand = "";
if ($(this).data("valuetype") === "inflated") {
handlerCommand = "AddAverageSalary";
} else if ($(this).data("valuetype") === "actual") {
handlerCommand = "UpdateAverageSalary";
}
$.get("WS/AverageSalary.ashx", { command: handlerCommand, studio_id: studioId, discipline_id: disciplineId, seniority_id: seniorityId, base_date: baseDate, currency: currency, salary: newSalary, vacation_pct: vacationPct, gv_admissible: gvAdmissible }).done(function (data) {
if (data != "1") {
$(this).removeClass("editable-unsaved");
allChangesSucceeded = true;
}
else {
alert('fail');
allChangesSucceeded = false;
}
});
});
if(allChangesSucceeded) CancelChanges();
}
}
function CancelChanges() {
var href = window.location.href;
href = href.split('#')[0];
window.location.href = href;
}
You could try using Promises and jQuery $.when
Store a list of the ajax call promises:
var defereds = [];
$(".editable-unsaved").each( function() {
//...
defereds.push($.get("WS/AverageSalary.ashx" /*...*/));
}
$.when.apply($, defereds).done(function() {
CancelChanges();
});
This should, hopefully, wait for all the ajax calls to finish before calling CancelChanges()
I think you need to change your structure a little bit, using a counter and calling CancelChanges when the counter equals the number of calls.
function SaveChanges() {
if (PreSaveValidation()) {
var studioId = $("#param_studio_id").val();
var baseDate = $("#param_selected_month").val().substring(6, 10) + $("#param_selected_month").val().substring(0,2);
var currency = "CAD";
var vacationPct = null;
var gvAdmissible = null;
var editableUnsaveds = $(".editable-unsaved"); //cache the selector here, because selectors are costly
var numOfGetsReturned = 0;
editableUnsaveds.each( function() {
var newSalary = $(this).text();
var disciplineId = $(this).data("disciplineid");
var seniorityId = $(this).data("seniorityid");
var handlerCommand = "";
if ($(this).data("valuetype") === "inflated") {
handlerCommand = "AddAverageSalary";
} else if ($(this).data("valuetype") === "actual") {
handlerCommand = "UpdateAverageSalary";
}
$.get("WS/AverageSalary.ashx", { command: handlerCommand, studio_id: studioId, discipline_id: disciplineId, seniority_id: seniorityId, base_date: baseDate, currency: currency, salary: newSalary, vacation_pct: vacationPct, gv_admissible: gvAdmissible }).done(function (data) {
if (data != "1") {
$(this).removeClass("editable-unsaved");
}
else {
alert('fail');
}
if(editableUnsaveds.length === ++numOfGetsReturned){
CancelChanges(); //now it should call when the final get call finishes.
}
});
});
}
}
function CancelChanges() {
var href = window.location.href;
href = href.split('#')[0];
window.location.href = href;
}
I'd use promises. The q library is my favorite way to implement them. But since you're using JQuery, I'd recommend following a similar approach to what I outline below, but using $.when, instead of q.allSettled
I often use promises when scraping tons of websites at once -- I need to iterate through a long list of websites, make requests for content, and do something with the content when the requests return. The last thing I want to do is send requests one at a time, handling each one as it returns.
In the abstract, that looks something like this:
function scrapeFromMany() {
var promises = [];
_.forEach(urls, function(url) {
// this makes the request
var promise = scraper(url);
// this stores the promise with the others you iterate through
promises.push(promise);
});
q.allSettled(promises).then(function(res) {
// this function is executed when all of the promises (requests) have been resolved
console.log("Everything is done -- do something with the results.", res);
});
}
Fwiw, promises aren't that easy to grok if you've never used them. If that's the case, plan on spending some time getting up to speed with the concepts. They'll change (for the much much better) the way you write async javascript, and they really are the blessed path with these sorts of operations.
Asynchronously call your check function within the "done" function handler. Keep track of how many requests have completed, and only do your processing once that's equal to the total number of expected requests.
if (PreSaveValidation()) {
var allChangesSucceeded = true;
var length = $(".editable-unsaved").length;
var completedCount = 0;
// ...
$(".editable-unsaved").each( function() {
// ...
$.get("WS/AverageSalary.ashx", data).done(function (data) {
completedCount++;
if (data != "1") {
$(this).removeClass("editable-unsaved");
// don't set all changes succeeded to true here
}
else {
alert('fail');
allChangesSucceeded = false;
}
isComplete(length, completedCount, allChangesSucceeded);
});
});
}
function isComplete(totalLength, currentLength, allChangesSucceeded) {
if (currentLength == totalLength) {
// should this be !allChangesSucceeded?
if (allChangesSucceeded) CancelChanges();
}
}
This happens because you are not waiting for the requests to complete to proceed with the loop.
To achieve so you have to set the "async" flag to false.
The call to the server should be like this:
$.ajax({
url: "WS/AverageSalary.ashx",
async: false,
data:{ command: handlerCommand, studio_id: studioId, discipline_id: disciplineId, seniority_id: seniorityId, base_date: baseDate, currency: currency, salary: newSalary, vacation_pct: vacationPct, gv_admissible: gvAdmissible },
success: function (data) {
if (data != "1") {
$(this).removeClass("editable-unsaved");
allChangesSucceeded = true;
}
else {
alert('fail');
allChangesSucceeded = false;
}
}
});
I am running a loop that updates records on a table but i need to wait for all of the records to update before i continue on.
How can I have jquery wait until all of the calls in the loopselectedrows function completes? I have read about .deferred and .when but i am not sure how to implement
either. they both do not seem to able to handle an array of calls if i where to change over to using an array for the ajax posts. Any help would be greatly appreciated.
this is the button that starts it all :
click: function () {
// validate all rows
var $selectedRows = $('#Table1').jtable('selectedRows');
LoopSelectedRows($selectedRows, 'Validate');
/// wait here until all ajax calls have completed
// then continue with checking
// check for any row with an error
var $ValidatedRows = $('#Table1').jtable('selectedRows');
var boolCheck = checkValidatedRows($ValidatedRows);
// if all records are succesfull then add them
// else alert user
if (boolCheck == true) {
LoopSelectedRows($selectedRows, 'Add');
}
else {
alert("Please correct invalid records and try again");
}
}
the first thing this does is take all of the records from the table and passes them to a looping function.
this is the looping function -
function LoopSelectedRows(SelectedRecords, actionType) {
if (SelectedRecords.length > 0) {
//Show selected rows
SelectedRecords.each(function () {
var record = $(this).data('record');
record.PERSON_NAME = record.PERSON_ID;
// Actions for loop
// Validation Action
if (actionType == 'Validate') {
check = validateRecord(record);
}
// call add function
if (actionType == 'Add') {
AddRecordToTable(record);
}
})
};
}
this loop can either validate or add records for now i am only worried about the validation function
this is the validation function:
function validateRecord(dataRecord) {
$.ajax({
url: "./ValidateAddRecord",
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(dataRecord),
success: function (data) {
if (data.Result == "OK") {
// update record with message
$('#table1').jtable('updateRecord', { record: data.Record });
}
else {
// display error
alert(data.Message);
}
}
});
}
One fairly clean way to know when multiple ajax calls are done is to use promises and jQuery's $.when(). That will give you a callback when all the ajax calls are done. It will take a little bit of reorganization of your code to use that.
First, you return the $.ajax() promise from validateRecord():
function validateRecord(dataRecord) {
return $.ajax({
url: "./ValidateAddRecord",
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(dataRecord),
success: function (data) {
if (data.Result == "OK") {
// update record with message
$('#table1').jtable('updateRecord', { record: data.Record });
}
else {
// display error
alert(data.Message);
}
}
});
}
Then, you collect all the promises in LoopSelectedRows() and return a master promise using `$.when():
function LoopSelectedRows(SelectedRecords, actionType) {
var promises = [];
if (SelectedRecords.length > 0) {
//Show selected rows
SelectedRecords.each(function () {
var record = $(this).data('record');
record.PERSON_NAME = record.PERSON_ID;
// Actions for loop
// Validation Action
if (actionType == 'Validate') {
promises.push(validateRecord(record));
}
// call add function
if (actionType == 'Add') {
promises.push(AddRecordToTable(record));
}
})
};
// return master promise
return $.when.apply($, promises);
}
Then, you can use that final promise to know when everything is done.
click: function () {
// validate all rows
var $selectedRows = $('#Table1').jtable('selectedRows');
LoopSelectedRows($selectedRows, 'Validate').then(function() {
// all the ajax calls in LoopSelectRows are done now
// check for any row with an error
var $ValidatedRows = $('#Table1').jtable('selectedRows');
var boolCheck = checkValidatedRows($ValidatedRows);
// if all records are succesfull then add them
// else alert user
if (boolCheck == true) {
LoopSelectedRows($selectedRows, 'Add');
} else {
alert("Please correct invalid records and try again");
}
});
FYI, you probably also want to change AddRecordToTable() to return a promise so it can work the same way (though it is not required because you aren't trying to wait for that operation to be done).
$.active returns the number of active Ajax requests. Use $.active==0 means no ajax requests are active. You could also use ajaxStart and ajaxStop to keep track of when requests are active.
Thank you jfriend00, your solution seems to have solved my issues.
Below is the updated version i am now using for anyone interested :
click: function () {
// validate all rows
var $selectedRows = $('#table1).jtable('selectedRows');
LoopSelectedRows($selectedRows, 'Validate').then(function () {
// check for any row with an error
var $ValidatedRows = $('#table1).jtable('selectedRows');
var boolCheck = checkValidatedRows($ValidatedRows);
// if all records are succesfull then add them
// else alert user
if (boolCheck == true) {
LoopSelectedRows($selectedRows, 'Add');
}
else {
alert("Please correct invalid records and try again");
}
});
}
// loop function
function LoopSelectedRows(SelectedRecords, actionType) {
var promises = [];
if (SelectedRecords.length > 0) {
//Show selected rows
SelectedRecords.each(function () {
var record = $(this).data('record');
// Actions for loop
// Validation Action
if (actionType == 'Validate') {
promises.push(validaterecord(record));
}
// call add function
if (actionType == 'Add') {
AddRecordToTable(record);
}
})
};
return $.when.apply($, promises);
}
// validate function
function validaterecord(dataRecord) {
var def = $.Deferred();
$.ajax({
url: "./ValidateAddRecord",
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(dataRecord),
success: function (data) {
if (data.Result == "OK") {
// update record with message
$('#table1).jtable('updateRecord', { record: data.Record });
// resolve token
def.resolve();
}
else {
// display error
alert(data.Message);
}
}
});
return def.promise();
}
I have 3 ajax call in one function and checkAjaxCompletion which checks each ajax completion flag.
What the code below does is send multiple separate ajax calls and interval method checks completion flags to determine whether to proceed or keep interval. (I know clearInterval is not shown but the point is I want to use something other than interval)
Current code is:
function manyAjax() {
setInterval( function() { checkAjaxCompletion(); } , 200);
ajax1();
ajax2();
ajax3();
}
function ajax1() {
//send ajax request to server and if success set flag to 1. Default is 0. Error is 2.
}
function ajax2() {
//send ajax request to server and if success set flag to 1. Default is 0. Error is 2.
}
function ajax3() {
//send ajax request to server and if success set flag to 1. Default is 0. Error is 2.
}
function checkAjaxCompletion() {
if(ajax1_flag == 1 && ajax2_flag == 1 && ajax3_flag == 1) {
//everything went success, do some process
}
else if(ajax1_flag == 2 || ajax2_flag == 2 || ajax3_flag == 2) {
//some ajax failed, do some process
}
else {
//all ajax have not been completed so keep interval i.e. do nothing here
}
}
But I'm hesitating to depend on using interval function because calling it so often seem such waste of memory. There must be better way to do. I'm thinking if observer pattern can be applied here but would like to hear opinions.
It is observer-notifier, if you want to call it that - but each of your ajax calls will more than likely have a callback in javascript when they complete. Why not call checkAjaxCompletion() at the end of each of them, and do nothing if you're still waiting on others?
Dustin Diaz does a great job with this example.
function Observer() {
this.fns = [];
}
Observer.prototype = {
subscribe : function(fn) {
this.fns.push(fn);
},
unsubscribe : function(fn) {
this.fns = this.fns.filter(
function(el) {
if ( el !== fn ) {
return el;
}
}
);
},
fire : function(o, thisObj) {
var scope = thisObj || window;
this.fns.forEach(
function(el) {
el.call(scope, o);
}
);
}
};
The publisher:
var o = new Observer;
o.fire('here is my data');
The subscriber:
var fn = function() {
// my callback stuff
};
o.subscribe(fn);
To unsubscribe:
var fn = function() {
// my callback stuff
};
o.subscribe(fn);
// ajax callback
this.ajaxCallback = function(){
$.ajax({
type: "POST",
url: ajax.url,
data: {key: value},
async : !isAll,// false使用同步方式执行AJAX,true使用异步方式执行ajax
dataType: "json",
success: function(data){
if(data.status == 'successful'){
selfVal.parent().find('.msg').addClass('ok').html(msg.ok);
}else if(data.status == 'failed'){
checkRet = false;
selfVal.parent().find('.msg').removeClass('ok').html(msg.error);
}else{
checkRet = false;
}
return this;
}
});
}
return this;
Maybe you want to check your inputvalue callback ajax in your form;
You can view my website Demo, hope help you.
http://6yang.net/myjavascriptlib/regForm
Okay my idea was to make your own object that can handle sending an array of requests, keep a history of each request and do what i'm gonna call 'postProccessing' on each response, here is a probably very dodgy bit of code to hopefully demonstrate what I am thinking.
var Ajax = function() {
var request, callback, lst;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else if (window.ActiveXObject) {
request = new ActiveXObject("Microsoft.XMLHTTP");
}
request.onreadystatechange = handleResponse;
this.history = [{}];
this.send = function(args) {
for (var i = 0; i < args.length; i++) {
if (args.url) {
request.open(args.type || 'GET', args.url);
}
request.send(args.data || null);
callback = args.callback;
lst++;
}
}
function handleResponse() {
var response = {
url: '',
success: true,
data: 'blah'
};
history.push(response);
if (postProccess()) {
callback();
}
}
function postProcess() {
if (this.history[lst].success) {
return true;
} else {
return false;
}
}
}
I have a the following java script object
function eventTypeObj() {
allEventTypes = [];
// When the object is created go and get all the event types that can be included in journey or clusters.
$.ajax({
url: "/ATOMWebService.svc/GetDisplayEventTypes",
dataType: "json",
success: function(result) {
allEventTypes = eval("(" + result.d + ")");
}
});
// Returns a list of all the event type IDS.
this.getEventTypeIds = function() {
var eventTypeIDs = [];
for (var i = 0; i < allEventTypes.length; i++) {
eventTypeIDs.push(allEventTypes[i].Id);
}
return eventTypeIDs;
};
}
I was wondering if there is a way stop some one calling the eventTypeObj.getEventTypeIds(); before the ajax call in the constructor has succeeded, and there is no data in the allEventTypes array?
Something like this would be way better (im not guaranteeing this is 100% working, but the concept is sound):
function eventTypeObj() {
this.allEventTypes = [];
this.hasLoadedEventTypes = false;
var loadEventTypes = function(cb) {
$.ajax({
url: "/ATOMWebService.svc/GetDisplayEventTypes",
dataType: "json",
success: function(result) {
this.allEventTypes = eval("(" + result.d + ")");
this.hasLoadedEventTypes = true;
cb();
}
});
};
this.getEventTypeIds = function(updateEventTypes, callback) {
var _getEventTypeIds = function() {
var eventTypeIDs = [];
for (var i = 0; i < this.allEventTypes.length; i++) {
eventTypeIDs.push(this.allEventTypes[i].Id);
}
return eventTypeIDs;
};
if (!this.hasLoadedEventTypes || updateEventTypes) {
loadEventTypes(function(){ callback(_getEventTypeIds()); });
}
else callback(_getEventTypeIds());
};
}
Example usage:
var eto = new eventTypeObj();
eto.getEventTypeIds(false, function(eventTypeIdArray) {
// do stuff with the id array
});
/*
somewhere later on you want to get an updated eventTypeId array
in case the event types have changed.
*/
eto.getEventTypeIds(true, function(eventTypeIdArray) {
// do stuff with the updated ids
});
var allowCall = false;
function eventTypeObj() {
allEventTypes = [];
// When the object is created go and get all the event types that can be included in journey or clusters.
$.ajax({
url: "/ATOMWebService.svc/GetDisplayEventTypes",
dataType: "json",
success: function(result) {
allEventTypes = eval("(" + result.d + ")");
allowCall = true;
}
});
// Returns a list of all the event type IDS.
this.getEventTypeIds = function() {
if(!allowCall) return; // or pop up a message
var eventTypeIDs = [];
for (var i = 0; i < allEventTypes.length; i++) {
eventTypeIDs.push(allEventTypes[i].Id);
}
return eventTypeIDs;
};
}
Or just check if allEventTypes is empty or not.
There is no way to prevent someone from calling it too soon. What would you want to have happen if they call it too soon?
It looks like your code now currently returns an empty array if allEventTypes hasn't yet been filled in. You can decide whether the empty array is the right result or if you should throw an exception when it's called too early to make it absolutely clear to the caller that the data is not yet available.
You could provide some helper code for people who need that information, but it might not yet be available. For example, you could allow them to register a callback that would get called from the success handler after the data had been filled in. You could allow them to query whether the data is available yet.
If you don't want the responsibility for the timing to be on the callers, then you cannot offer a synchronous way to get this information. Instead, you would only offer a callback mechanism for getting the data. If the data is ready, the callback would get called immediately. If the data is not ready, the callback would get called when the ajax function completes. In either case, the caller would have to process the data in the callback only and getEventTypeIds would not be a normal call to get the data like it is now, but rather a call to register a callback that would be called with the data when was ready. This would relieve the caller from having to know implementation details of when the data was ready, but would force them to use the asynchronous nature of the callback mechanism.
this.getEventTypeIds = function(callback) {
if (allEventTypes.length > 0) {
// data is ready call the callback with the data now
} else {
// store the callback to be called later from the success handler
}
}
You can check if the eventType array is empty, right?
if(allEventTypes.length == 0)
{
return;
}