Javascript: Variable value never changed in a loop - javascript

This is the code:
var link, summary;
for (var i = 0; i < json.cards[0].widgets.length; i++) {
link = json.cards[0].widgets[i].text;
var params = {
// various parameters
};
var req = http.request(params, function(res) {
res.on('data', function(data) {
summary = JSON.parse(data.toString()).content;
// Now, the process method takes "summary" correctly,
// but "link" is always the same, hence, not changing
// on every other cycle.
process(summary, link);
// That's how it would look if looped 3 times
// process('positive', 'http://www.google.com/blah');
// process('negative', 'http://www.google.com/blah');
// process('neutral', 'http://www.google.com/blah');
});
});
req.end();
}
As I commented in the code, link never changes, it takes the first value, then process() takes the same value of link all the time, while summary is always different, which is expected.

Alon Eitan helped me out by suggesting to separate it in a separate function and it worked. That's how my code looks now:
var link, summary;
for (var i = 0; i < json.cards[0].widgets.length; i++) {
link = json.cards[0].widgets[i].text;
var params = {
// various parameters
};
var sent = function(link, params){
var req = http.request(params, function(res) {
res.on('data', function(data) {
summary = JSON.parse(data.toString()).content;
process(summary, link);
});
});
req.end();
}
sent(link, params);
}

req is an asynchronous function, so there is no guarantee regarding what order it will return in with comparison to the outer loop. As such, it's almost certain in this case that the outer loop finishes before the request even returns, so link is always the same.
Instead of doing this, try using promises, or pass the value of link as a parameter to the request so that it will have the right value for each return.

Related

Can I stop an event handler while it's executing?

Is it possible to stop the execution of a previous event when the event is called again?
To clarify, I have a button <button onclick='load()'>load</button> that calls a load() function which gets an array, processes each element and displays it in a list <ul id='main'></ul>
function load(event) {
$("#main").empty(); //empty old elements
$.get("load.php", '', function (data) {
var arr = JSON.parse(data);
for (i = 0; i < arr.length; ++i) {
process(arr[I]); //process and append to #main
}
});
}
Problem is, that if I click the button again while its still putting the elements into the array, I get the new list plus the rest of the old list.
Is there a way to stop the first event while its still executing but still execute the second event?
You should try this:
var xhr;
function load(ev){
if(ev.eventPhase === 2){
if(xhr)xhr.abort();
$('#main').empty();
xhr = $.get('load.php', function(data){
var a = JSON.parse(data);
for(var i=0,l=a.length; i<l; i++){
process(a[i]);
}
});
}
}
I can be wrong, but...
var req = $.ajax({
$("#main").addEventListener("click",()=>{req.abort()})
...
...
$("#main").removeEventListener("click",()=>{req.abort()})
});
As noted, you can stop the event by setting a flag and checking it, but a better approach would simply be to assign the new value directly. If your code works it means JSON.parse is returning an array already.
That means
"use strict";
(function () {
function load(event) {
$("#main").empty();
$.get("load.php", '', function (data) {
process = JSON.parse(data);
$("#main").whateverMethodFillsTheElement(process);
});
}());
Also, when writing asynchronous JavaScript code that makes HTTP requests, promises are preferred to callbacks. Since $.get returns a Promise you can write
"use strict";
(function () {
function load(event) {
$("#main").empty();
$.get("load.php")
.then(function (data) {
var items = JSON.parse(data);
$("#main").whateverMethodFillsTheElement(items);
});
}
}());
As discussed in comments, the aim is to use each item in another request which provides the actual value to add to 'main'. So loading data triggers an asynchronous call for each loaded item.
To accommodate this, we need to determine a key field that we can use to track each item so we do not append existing items to the list. We will call this field id for the sake of exposition.
"use strict";
(function () {
var allItems = [];
function load(event) {
$("#main").empty();
$.get("load.php")
.then(function (data) {
return JSON.parse(data);
})
.then(function (items) {
items.forEach(item => {
processItem(item)
.then(function (processed) {
var existingItem = allItems.filter(i => i.id === item.id)[0];
if(existingItem) {
var existingIndex = allItems.indexOf(existingItem);
allItems[existingIndex] = processed;
}
else {
allItems.push(processed);
}
});
});
});
}
}());
Ok, seems like it's not possible to stop an Ajax success function after it began executing or to stop a past event without aborting the current one.
But the following solution worked for me so I figured I'll post it here:
var num = 0;
function load() {
var curNum = ++num;
$("#main").empty();
$.get("load.php", '', function (data) {
var arr = JSON.parse(data);
for (i = 0; i < arr.length; ++i) {
process(arr[i], curNum);
}
});
}
function process(item, curNum) {
if(curNum === num) { //don't process if a new request has been made
//get 'data' based on 'item'...
if(curNum === num) { //check again in case a new request was made in the meantime
$("#main").append(data);
}
}
}
I appreciate everyone's help.

Parse.Cloud.job promise not working

What I am trying to do here are:
Remove all contents in a class first, because every day the events.json file will be updated. I have my first question here: is there a better way to remove all contents from a database class on Parse?
Then I will send a request to get the events.json and store "name" and "id" of the result into a 2D array.
Then I will send multiple requests to get json files of each "name" and "id" pairs.
Finally, I will store the event detail into database. (one event per row) But now my code will terminate before it downloaded the json files.
Code:
function newLst(results) {
var event = Parse.Object.extend("event");
for (var i = 0; i < results.length; i++){
Parse.Cloud.httpRequest({
url: 'https://api.example.com/events/'+ results[i].name +'/'+ results[i].id +'.json',
success: function(newLst) {
var newJson = JSON.parse(newLst.text);
var newEvent = new event();
newEvent.set("eventId",newJson.data.id);
newEvent.set("eventName",newJson.data.title);
newEvent.save(null, {
success: function(newEvent) {
alert('New object created with objectId: ' + newEvent.id);
},
error: function(newEvent, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
},
error: function(newLst) {
}
});
}
};
Parse.Cloud.job("getevent", function(request, status) {
var event = Parse.Object.extend("event");
var query = new Parse.Query(event);
query.notEqualTo("objectId", "lol");
query.limit(1000);
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
var myObject = results[i];
myObject.destroy({
success: function(myObject) {
},
error: function(myObject, error) {
}
});
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
var params = { url: 'https://api.example.com/events.json'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
var tmp2D = {"name":"id"}
tmp2D.name = [jsonobj.data[i].name];
tmp2D.id = [jsonobj.data[i].id];
results.push(tmp2D);
}
newLst(results);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
I think my original answer is correct as a standalone. Rather than make it unreadable with the additional code, here it is made very specific to your edit.
The key is to eliminate passed callback functions. Everything below uses promises. Another key idea is decompose the activities into logical chunks.
A couple of caveats: (1) There's a lot of code there, and the chances that either your code is mistaken or mine is are still high, but this should communicate the gist of a better design. (2) We're doing enough work in these functions that we might bump into a parse-imposed timeout. Start out by testing all this with small counts.
Start with your question about destroying all instances of class...
// return a promise to destroy all instances of the "event" class
function destroyEvents() {
// is your event class really named with lowercase? uppercase is conventional
var query = new Parse.Query("event");
query.notEqualTo("objectId", "lol"); // doing this because the OP code did it. not sure why
query.limit(1000);
return query.find().then(function(results) {
return Parse.Object.destroyAll(results);
});
}
Next, get remote events and format them as simple JSON. See the comment. I'm pretty sure your idea of a "2D array" was ill-advised, but I may be misunderstanding your data...
// return a promise to fetch remote events and format them as an array of objects
//
// note - this differs from the OP data. this will evaluate to:
// [ { "name":"someName0", id:"someId0" }, { "name":"someName1", id:"someId1" }, ...]
//
// original code was producing:
// [ { "name":["someName0"], id:["someId0"] }, { "name":["someName1"], id:["someId1"] }, ...]
//
function fetchRemoteEvents() {
var params = { url: 'https://api.example.com/events.json'};
return Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var remoteEvents = JSON.parse(httpResponse.text).data;
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = { "name": remoteEvents[i].name, "id": remoteEvents[i].id };
results.push(remoteEvent);
}
return results;
});
}
Please double check all of my work above regarding the format (e.g. response.text, JSON.parse().data, etc).
Its too easy to get confused when you mix callbacks and promises, and even worse when you're generating promises in a loop. Here again, we break out a simple operation, to create a single parse.com object based on one of the single remote events we got in the function above...
// return a promise to create a new native event based on a remoteEvent
function nativeEventFromRemoteEvent(remoteEvent) {
var url = 'https://api.example.com/events/'+ remoteEvent.name +'/'+ remoteEvent.id +'.json';
return Parse.Cloud.httpRequest({ url:url }).then(function(response) {
var eventDetail = JSON.parse(response.text).data;
var Event = Parse.Object.extend("event");
var event = new Event();
event.set("eventId", eventDetail.id);
event.set("eventName", eventDetail.title);
return event.save();
});
}
Finally, we can bring it together in a job that is simple to read, certain to do things in the desired order, and certain to call success() when (and only when) it finishes successfully...
// the parse job removes all events, fetches remote data that describe events
// then builds events from those descriptions
Parse.Cloud.job("getevent", function(request, status) {
destroyEvents().then(function() {
return fetchRemoteEvents();
}).then(function(remoteEvents) {
var newEventPromises = [];
for (var i = 0; i < remoteEvents.length; i++) {
var remoteEvent = remoteEvents[i];
newEventPromises.push(nativeEventFromRemoteEvent(remoteEvent));
}
return Parse.Promise.when(newEventPromises);
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
The posted code does just one http request so there's no need for an array of promises or the invocation of Promise.when(). The rest of what might be happening is obscured by mixing the callback parameters to httpRequest with the promises and the assignment inside the push.
Here's a clarified rewrite:
Parse.Cloud.job("getevent", function(request, status) {
var promises = [];
var params = { url: 'https://api.example.com'};
Parse.Cloud.httpRequest(params).then(function(httpResponse) {
var results = [];
var jsonobj = JSON.parse(httpResponse.text);
for (var i = 0; i < jsonobj.data.length; i++) {
// some code
}
}).then(function() {
status.success("run job");
}, function(error) {
status.error(error);
});
});
But there's a very strong caveat here: this works only if ("// some code") that appears in your original post doesn't itself try to do any asynch work, database or otherwise.
Lets say you do need to do asynch work in that loop. Move that work to a promise-returning function collect those in an array, and then use Promise.when(). e.g....
// return a promise to look up some object, change it and save it...
function findChangeSave(someJSON) {
var query = new Parse.Query("SomeClass");
query.equalTo("someAttribute", someJSON.lookupAttribute);
return query.first().then(function(object) {
object.set("someOtherAttribute", someJSON.otherAttribute);
return object.save();
});
}
Then, in your loop...
var jsonobj = JSON.parse(httpResponse.text);
var promises = [];
for (var i = 0; i < jsonobj.data.length; i++) {
// some code, which is really:
var someJSON = jsonobj.data[i];
promises.push(findChangeSave(someJSON));
}
return Parse.Promise.when(promises);

variable scope in module asynchronous function

This is my first week in node so I'm sorry if this is a no brainier.
The code works and does what it should. But I can't figure out how to match the name (url) that starts http.get whit the result it gets from the website.
I found this witch is almost like my problem, except this is a premade function so I can't edit the function and add a callback.
variable scope in asynchronous function
If I could run this code synchronous or make a callback in the http.get function it would all be good. But I don't have the skills and don't know if you even can do it.
Thanks
- Robin.
http = require('http');
function download(name) {
//name is an array whit csgo items names.
for (var i = 0; i < name.length; i++) {
var marketHashName = getGoodName(name[i]);
var url = 'http://steamcommunity.com/market/priceoverview/?currency=1&appid=730&market_hash_name=' + marketHashName;
http.get(url, function (res) {
var data = "";
res.on('data', function (chunk) {
data += chunk;
});
res.on("end", function () {
data = JSON.parse(data);
var value= 0;
//get the value in the json array
if(data.median_price) {
value = data.median_price;
}else{
value = data.lowest_price;
}
value = value.substr(5);
console.log("WEAPON",value);
//callback whit name/link and value?
//callback(name,value);
});
}).on("error", function () {
});
}
}
You can just add a callback argument and then call it with the final data. And, if you want to pass to the callback the particular marketHashName that was being processed, then you can create a closure to capture that uniquely for each time through the for loop:
http = require('http');
function download(name, callback) {
//name is an array whit csgo items names.
for (var i = 0; i < name.length; i++) {
var marketHashName = getGoodName(name[i]);
// create closure to capture marketHashName uniquely for each
// iteration of the for loop
(function(theName) {
var url = 'http://steamcommunity.com/market/priceoverview/?currency=1&appid=730&market_hash_name=' + marketHashName;
http.get(url, function (res) {
var data = "";
res.on('data', function (chunk) {
data += chunk;
});
res.on("end", function () {
data = JSON.parse(data);
var value= 0;
//get the value in the json array
if(data.median_price) {
value = data.median_price;
}else{
value = data.lowest_price;
}
value = value.substr(5);
console.log("WEAPON",value);
// now that the async function is done, call the callback
// and pass it our results
callback(theName, value, data);
});
}).on("error", function () {
});
})(marketHasName);
}
}
// sample usage:
download("whatever", function(name, value, data) {
// put your code here to use the results
});
FYI, you may find that the request module which is a higher level set of functionality on top of the http module will save you some work.

Javascript : return XMLHttpRequest out of scope

Javascript : return XMLHttpRequest out of scope
I need to return the data from my AJAX call
series: [{
data: ( )
in order to update one of the keys data in dictionary series but my function retrieve does not seem to return the data that I am getting.
var myPopulation = {
series: [{
data: (
function() {
function retrieve() {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var obj = JSON.parse(httpRequest.responseText)
console.log(obj.V1, obj.V2, obj.V3, obj.V4);
var data = [],
time = (new Date()).getTime(),
i;
for (i = -60; i <= 0; i++) {
console.log(obj.V2)
data.push({
x: time + i * 60 * 1000,
y: obj.V2
});
}
myPopulation.series[1].data = data
// ???
console.log(data)
}
}
};
httpRequest.open('GET', "/myCall");
httpRequest.send();
}
retrieve();
}()
)
}],
What should I do to return the value out of the function and update data?
Well, since you are using jQuery tag, I think my answer could be valid and I prefer doing this way for what you need and I understood (it is well explained so please read code comments and check browser console, this can be found at the end of the answer).
Remember that you won't be able to return a XMLHttpRequest because ajax calls are async but you can force an ajax call to be sync in order to get your data on a return statement from any function or do other things as you expected. However, forcing is not a good approach to do because UI will freeze for the user until getting the response back from server and you really don't know how much time that will take (specially if you are expecting a big amount of data to be returned - I know that's not entirely a metric but other factors may apply).
Hope this helps and please take your time and read the following post and user comments: Reasons not to use native XMLHttpRequest - why is $.ajax mandatory?
Live Demo: http://jsfiddle.net/4mbjjfx8/
HTML:
<div id="wrapper">
<div id="loader"></div>
<div id="content"></div>
</div>
jQuery
$(function() {
var series = [], // Your series array
loader = $('#loader'), // UI loader sample
request = {}; // Request params
/**
* Set request method, url and data if needed
* In this case I am sending an object with a text property
* that will be returned from jsfiddle "echo" service
*/
request.method = 'GET';
request.url = '/echo/jsonp/';
request.data = {
text: 'Second message returned from "echo" service'
};
// Send ajax call
retrieveData(request, series, handleData);
// Set loading message to UI
loader.html('Loading...');
// Just do some logging to know how process goes
console.log('Populating series for the first time');
/**
* Populate series for the first time, at this point process
* will go on and after the response from server was finally
* done, process will go to the callback (since ajax calls
* are async).
*/
populate(series);
// Just do some logging to know how process goes
console.log('End populating series for the first time');
});
function populate(series) {
var dummy = {
text: 'First message populated over process'
};
// Set dummy object to series array
series.push(dummy);
};
/**
* Used to make ajax call and return data from server
*/
function retrieveData(cfg, series, callback) {
$.ajax({
type: cfg.method,
url: cfg.url,
data: cfg.data
}).done(function(data, status, xhr) {
// Pass args to callback function if defined
if (callback) callback(series, data);
}).fail(function(xhr, status) {
/**
* Pass args to callback function if defined
* At this point, request wasn't success so
* force data arg at callback to be 'null'
*/
if (callback) callback(series, null);
});
};
/**
* Used to handle data returned from server
* Note: Your series array can be modified here since you
* passed it into the callback
*/
function handleData(series, data) {
var loader = $('#loader');
// Just do some logging to know how process goes
console.log('Populating series from server');
// Check if data is defined and not an empty object
if(data && !($.isEmptyObject(data))) {
// Add it to series array
series.push(data);
}
// Set UI loader empty
loader.html('');
// Retrieve series
showData(series);
};
function showData(series) {
var contentDiv = $('#content');
// Loop process and append to UI
for(var i = 0; i < series.length; ++i) {
contentDiv.append(series[i].text + '<br>');
}
};
You should put retrieve function outside. You can invoke retrieve function. And, It will call ajax. When ajax is success, it will update data of population. Like this.
var myPopulation = {
series: [{
data: undefined
}]
};
function retrieve() {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var obj = JSON.parse(httpRequest.responseText)
console.log(obj.V1, obj.V2, obj.V3, obj.V4);
var data = [],
time = (new Date()).getTime(),
i;
for (i = -60; i <= 0; i++) {
console.log(obj.V2)
data.push({
x: time + i * 60 * 1000,
y: obj.V2
});
}
myPopulation.series[0].data = data
// ???
console.log(data)
}
}
};
httpRequest.open('GET', "/myCall");
httpRequest.send();
}
retrieve();
Assuming you're simply trying to set the value of myPopulation.series[0].data when the array is first defined...
myPopulation.series[1].data = data
...should be...
myPopulation.series[0].data = data;
Also, some parts of you code are missing closing semicolons, closing brackets and/or curly brackets. Please make sure you end all statements with a semicolon and you have an equal number of opening and closing (curly) brackets.
I've tested your code with the above changes. The HTTP request I made returned a simple "Test successful" string, so I've replaced the code which handles the structure of the response text to simply var data = httpRequest.responeText;. This worked fine. Of course, this assumes the code which handles the structure of the returned httpRequest.responeText in your case is correct, as I have no way of knowing what the responseText in your case looks like. If you receive any errors regarding this part of your code, we'll need to see what the responseText looks like before we can help you.
I'm not judging whether you are doing the right thing. Im merely presenting you a working version of your code.
Errors in your code:
You mean to set result of the "function" to data but your function is not returning anything in the first place.
XMLHttpRequest is async so even if you return you will not have the data set, simply because the outer function exited after making the http request setting a callback to trigger when it is completed.
Note: The fix is by making XMLHttpRequest synchronous.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests
Here is the corrected version of your code
var myPopulation = {
series: [{
data: (
function() {
function retrieve() {
var httpRequest = new XMLHttpRequest();
var result = []; //[1] just renamed data to result to avoid confusion
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var obj = JSON.parse(httpRequest.responseText)
console.log(obj.V1, obj.V2, obj.V3, obj.V4);
var time = (new Date()).getTime(),
i;
for (i = -60; i <= 0; i++) {
console.log(obj.V2)
result.push({
x: time + i * 60 * 1000,
y: obj.V2
});
}
//myPopulation.series[1].data = data //[2] commented this as it is not necessary
// ???
console.log(result)
}
}
};
httpRequest.open('GET', "/myCall", false); //[3] Added 3rd argument 'false' to make the call synchronous
httpRequest.send();
return result //[4] to convey the result outside
}
return retrieve(); //[5] added return to set it to the data
}()
)
}],
The above code is not tested however. Here is a tested solution http://jsfiddle.net/98f9amo8/1/
The jsfiddle content is different for obvious reasons.
Working with async code means you have to change the way you code because the code is not executed top-down any more.
So, in your case, you would do something like:
var myPopulation = {series: []};
$.get(..., my_function_that_will_format_the_data_after_they_have_been_received);
...
my_function_that_will_format_the_data_after_they_have_been_received() {
// Do stuff here
var formattedData = ...
myPopulation.series.push(formattedData);
// ONLY NOW, myPopulation is ... populated with data.
// So, whatever you use this for, need to be called here
doMagicWith(myPopulation);
}
...
/// Here, myPopulation is empty. doMagicWith(myPopulation) will fail here.
I do not know the context of how you are doing this, seeing no jQuery tells me you wish to avoid it.
So no matter what happens the call is going to take time, and you need to wait for it for whatever you may need to do with it. Loaders can help tell a user that its processing but there are other ways to do that as well. The common factor is no matter what the data is not going to be there when you need it unless you do some sort of callback.
So here is an idea, create your on onload event more or less. There are many things to keep an eye on so jQuery's is probably the most complete, but going to keep it simple here.
window.isLoaded = false;
window.ajaxLoaded = false;
window.onload = function(){
if(window.ajaxLoaded){
onReadyFunction();
}else{
window.isLoaded = true;
}
}
//skipping most of your code, the key part is the retrieve function.
//So its the only part I am going to include in this part.
function retrieve() {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var obj = JSON.parse(httpRequest.responseText)
console.log(obj.V1, obj.V2, obj.V3, obj.V4);
var data = [],
time = (new Date()).getTime(),
i;
for (i = -60; i <= 0; i++) {
console.log(obj.V2)
data.push({
x: time + i * 60 * 1000,
y: obj.V2
});
}
myPopulation.series[1].data = data
// ???
console.log(data)
}
}
//here is my only addition
if(window.isLoaded){
onReadyFunction();
}else{
window.ajaxLoaded = true;
}
};
httpRequest.open('GET', "/myCall");
httpRequest.send();
}
So all I am doing is adding another part to the typical DOM load. Waiting for the data you need to be available before it initialized the rest of the JS. Doing this you can keep the least downtime for your app (although it depends on where you are trying to get this data though). All you need is to define the onReadyFunction like so.
function onReadyFunction(){
//all the rest of your JS here
}
This can be expanded and organized very easy, just a simple example to get started.

How can I stop an object method call before the ajax has completed

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;
}

Categories