var not accessible/visible within $.getJSON - javascript

First off, know that I am very new noob. The code below works except for the "if" portion of the "if-else" portion. The "searchTerm" that is alerted is the last value that a user enters into an input field with multiple values. So if user enters "a, b, c, d", and "a" (or any other value) meet the ===0 criteria, "d" is what is alerted.
I have researched here in stackoverflow and googled other areas and learning about callbacks and promises and that .getJSON is asynchronous and visibility outside of JSON and such but I have to admit I'm obviously not getting it and I'm sure I'm missing something simple. Any insight/help will be greatly appreciated.
for (var l = 0; l < searchTermArray.length; l++) {
searchTerm = searchTermArray[l];
searchURL = buildURL(searchTerm);
getResults(searchURL, searchTerm);
function getResults() {
$.getJSON(searchURL, function (responses) {
presentResults(responses, searchTerm);
});
}
function presentResults(responses, searchTerm) {
response = responses.search;
if (responses.search.return.count === 0) {
alert(searchTerm + " Not Found");
} else {
alert("Results found");
****Do other stuff with the results****
}
}
}

Your issue is that for loop is not waiting for your asynchronous getjson. Searchurl and search term is overridden by every iteration. That is why you always get the last search term. Write a function and pass all the search urls and callback from it.
I hope this will lead you to the solution.
PS: Your function definitions are also inside the for loop which is not preferred.

I went ahead and separated the functions away from the for loop and then wrapped the .getJSON in a function of its own and passed the searchURL and searchTerm parameters which allowed me to works with the searchTerm for which the criteria was met.
function getResults(searchURL, searchTerm) {
responses = '';
$.getJSON(searchURL, function (responses) {
presentResults(responses, searchTerm);
}); // close JSON
}

Related

How to increment skip count until results is found in API?

I have the following api which returns thousands of result: http://xxx/xxx/CarRoutes. The API creator however limits only 50 results to be return at once. Hence, to see get another 50 more results to be returned, "?$skip=50" needs to be used. Also, the api url does not allow to add in any parameters behind.
Now I would like to search for CarRoutes id = 123. How can I auto increment the $skip count until results is found?
Appreciate if it can be done Javascript language.
Current idea I have, which is not efficient.
function getInfo() {
$.ajax({
url: "http://xxx/xxx/CarRoutes?$skip="+skip,
success: function(result) {
var obj = JSON.stringify(result);
var routetimeobj = JSON.parse(obj);
var data = routetimeobj['value'];
var data_filter = data.filter(element => element.CarRoute =="123");
if(data_filter.length==0){
skip+=50;
getInfo();
return;
}
});
};
If you want to follow the pattern you are using in your code, you can add a parameter to your function
function getInfo(skip)
and when you are calling again, call it like this
getInfo(skip + 50);

How to use multiple callbacks when looping data

I'm trying to get HTML form data, loop it through, change it a bit and insert it to database. I have tried like below app.js.
How can I make callbacks so that formdata what I have modified is available for .create function?
I have searched from everywhere and I always end up in dead end and undefined variable somehow.
app.js:
//Find the day where to save
Day.findById(req.params.id, function(err, day) {
if (err) {
console.log(err);
res.redirect("/diary");
} else {
// Search function to find data with _id
function ingredientIdQuery(reqBodyId) {
var ingQuery = Ingredient.find({_id:reqBodyId});
return dbQuery;
}
// This loops through HTML formdata and formats it for mongoose model
for (var i = 0; i < req.body.amount.length; i++) {
if (req.body.amount[i] !== "") {
var amount = Number(req.body.amount[i]);
var singleMealTempObj = {};
singleMealTempObj.amount = amount;
var _id = req.body.id[i];
var query = ingredientIdQuery(_id);
// Executing the query for the data I need with id
query.exec(function(err, ingr){
if(err) {
return console.log(err);
} else {
singleMealTempObj.ingredient = ingr[0];
singleMealTempArr.push(singleMealTempObj);
}
});
}
}
}
// This inserts data into day
Meal.create(singleMealTempArr, function(err, singleMealObject) {
if (err) {
console.log(err);
} else {
day.meals.push(singleMealObject);
day.save();
res.redirect("/day/" + day._id + "/dayshow");
}
});
});
});
Edit:
Thanks for reply and notices! While I was trying to do everything to get this work I missed those few things like declaring variables. Sorry for that. I threw the towel in to the cage at this point.
flow goes like this:
User sends HTML form data to app.js which is inside object of two arrays (id[] and amount[]). Amount array needs to be looped through if it has value other than 0. Same index id array value is used to fetch data from database. This data what is found from database with id from id[] is used with same index amount[] and it should be saved to mongo.
I can get the values from HTML form ok. but I have tried to make a search in Mongo in a for loop (query.exec in the code) I get the data ok. When I log the data outside the database query, variable is undefined.
I hope this clarifys a bit what I'm trying to achieve.
I'll continue this later... :)
I guess issue originates because of this function.
function ingredientIdQuery(reqBodyId) {
var ingQuery = Ingredient.find({_id:reqBodyId});
return dbQuery;
}
Is find function asynchronous or synchronous?
Also you are returning dbQuery but dbQuery does not seem to be changed inside the function.
Couple I noticed that may fix this:
You never define singleMealTempArr, so when you try to push data to it, you are gonna run into problems.
Your ingredientIdQuery function returns dbquery - which also isn't defined. You actually call it ingQuery. Even so...are you positive that this will return the data that you want?
// lets loop through all the form fields in req.body.amount
for (var i = 0; i < req.body.amount.length; i++) {
// keep going unless the form field is empty
if (req.body.amount[i] !== "") {
// assign all the form fields to the following vars
var amount = Number(req.body.amount[i]);
var singleMealTempObj = {};
singleMealTempObj.amount = amount;
var _id = req.body.id[i];
var query = ingredientIdQuery(_id);
// we are executing the ingredientIdQuery(_id), better
// double-check that this query returns the result we are
// looking for!
query.exec(function(err, ingr){
if(err) {
return console.log(err);
} else {
singleMealTempObj.ingredient = ingr[0];
// now that we've gone through and mapped all the form
// data we can assign it to the singleMealTempArr
// WOOPS! Looks like we forgot to assign it!
singleMealTempArr.push(singleMealTempObj);
}
});
}
}
}

Ajax perform async requests synchronously [duplicate]

I know this question has been asked countless times, but I cant figure out for the life of me how to make this answer work in my case: wait for async javascript function to return
I'm looping through some "tv channels" in the outerloop and then looping through dates in the week in the innerloop. In the inner loop I make a ajax request to a server to fetch the data and I then store/cache it for later use like so
var dates = []; //<-- Contains a list of dates for the coming week
var baseUrl = "http://www.someserver.com";
var storedChannels = [1,2,3,4,5,6,7,8,9,10,45,23,56,34,23,67,23,567,234,67,345,465,67,34];
for(ch = 0; ch < storedChannels.length; ch++) {
var channel = storedChannels[ch];
for(d=0; d < 7; d++) {
var currentDate = dates[d];
ajax({
url: baseUrl+"?ch="+channel+"&dt=currentDate"+,
complete: function(res) {
CMLocalStore.setString('ch' + ch + "_" + scheduleDay, res);
},
});
//Want to wait here till the ajax request completes.
//Do not want to continue to next iteration.
//Do not want to fire of 50 bazillion ajax requests all at once
//Why? Very limited bandwidth scenario, plenty of channels
}
}
PS: NO JQuery please! Plain JS solutions only
Many thanks!
You want something like this. I haven't tested it, but hopefully you should get the idea.
var dates = []; //<-- Contains a list of dates for the coming week
var baseUrl = "http://www.someserver.com";
var storedChannels = [1,2,3,4,5,6,7,8,9,10,45,23,56,34,23,67,23,567,234,67,345,465,67,34];
function ProcessNext(ch, d) {
if (d < 7) {
d++;
} else {
d=0;
if (ch < storedChannels.length) {
ch++;
} else {
return;
}
}
var channel = storedChannels[ch];
var currentDate = dates[d];
ajax({
url: baseUrl+"?ch="+channel+"&dt=currentDate"+,
complete: function(res) {
CMLocalStore.setString('ch' + ch + "_" + scheduleDay, res);
ProcessNext(ch, d);
},
});
}
ProcessNext(0, 0);
You need to turn your loop into a chain of callbacks.
Instead of using a loop, you should make your callback call your original function, but with a higher parameter value.
What you are trying to do is explained in the Asynchronous Iteration Patterns tutorial by Pedro Teixeira. The examples are using Node.js but you can use the same patterns in the browser. Basically what you need to do is convert your loops to serial callbacks waiting on each other to complete, so the next AJAX request is fired from the success callback of the previous one etc. It can be done without blocking the browser but not in loops. See that tutorial.
Essentially the answer lies in using recursive calls instead of using loops. Just wanted to add this answer for anyone that might be interested in "for loop nestings" deeper than 2 levels. As you can see its easy to extend to as many "nestings" as you like.
Original credit goes to VatooVatoo implementation in Java on the DaniWeb forums.
Heres the code, tested and works (without the ajax bits of course but you can add that yourself):
<html>
<head>
<script type="text/javascript">
function loopRecurse(a, b, c)
{
if(c >= 2) {
b++;
c=0;
loopRecurse(a, b, c);
return;
}
if(b >= 2) {
a++;
b=0;
loopRecurse(a, b, c);
return;
}
if(a >= 2) return;
document.write("<div>" + a + "|" + b + "|" + c + "</div>");
c++;
loopRecurse(a, b, c);
}
loopRecurse(0, 0, 0);
</script>
</head>
<body>
<!-- output
0|0|0
0|0|1
0|1|0
0|1|1
1|0|0
1|0|1
1|1|0
1|1|1
-->
</body>
</html>
See the XMLHttpRequest documentation (linked to MDC, but shouldn't matter). Basically the condition you're looking for is request.readyState==4 - presuming that you have your ajax() return the actual XMLHttpRequest object.

For loop proceeding out of order

new at this, please tell me if I'm leaving information out or anything like that.
The code I'm working on can be seen here: http://codepen.io/hutchisonk/pen/mVyBde and I have also pasted the relevant section of javascript below.
I'm having trouble understanding why this code is behaving as it is. Quick outline - I have defined a few variables at the top, made a function that fetches the data I need and builds it into a pretty little list. This seems to be working as planned.
With the function outlined, I then loop through each "friend" in the "friends" array, calling the function once each time. I have numbered the friends on the output to help clarify what is going on. I have tried this a number of ways, including with the "for loop" syntax that's currently implemented, as well as the "forEach" syntax that's commented out.
Two main questions:
1) The number in front of each name is the "i" in my for loop. Why is this "i" not going in order from 0 to 10? How do I get it to do so? It appears to be in a different order every time the code is run. And, it repeats the numbers it has looped through previously on each new iteration. I would like to understand why this is happening.
2) The code seems to be running out of order. The unexpected behavior can be seen in the console.log - the for loop outputs the first two lines of console.log on a loop, then jumps out and console.logs the test variable "a" and the other text below the for loop, and then jumps back into the for loop and console.logs the output from the function. I'm looking at the console in google chrome and I did read that there can be timing inconsistancies with regard to the console, but I don't understand how the loop is being split in half - the first two lines, and then the function call being logged after the later code.
What is the best way to iterate through an array? Any insights on how to call a function within a loop correctly or resources you can provide are much appreciated.
$("document").ready(function(){
var friends = ["lainzero", "freecodecamp", "storbeck", "terakilobyte", "habathcx","RobotCaleb","thomasballinger","noobs2ninjas","beohoff", "dtphase", "MedryBW"];
var html = "";
var url = "";
function getStreamingData(eachfriend, number) {
url = "https://api.twitch.tv/kraken/streams/"+eachfriend;
$.ajax({
dataType: "jsonp",
url: url,
success: function(result) {
console.log(result+", "+result.stream);
if(result.stream !== null) {
html+= "<li class='streaming'><a href='twitch.tv/"+eachfriend+"'>"+number+": "+eachfriend;
html +="<i class='fa fa-play-circle style='font-size:20px;color:green;''></i>";
} else if (result.stream === null) {
html+= "<li class='not_streaming'><a href='twitch.tv/"+eachfriend+"'>"+number+": "+eachfriend;
html +="<i class='fa fa-stop-circle' style='font-size:20px;color:red;'></i>";
}
html +="</a></li>";
$("#all ul").append(html);
}//success
});//$ajax
}//getstreamingdata function
for (var i=0;i<friends.length;i++) {
console.log(i);
console.log(friends[i]);
getStreamingData(friends[i], i);
}
//Same as for loop above, but using forEach. This produces the same results.
/*
var i=0;
friends.forEach(function(friend) {
getStreamingData(friend, i);
i++;
});
*/
var a = 4;//testing console output
console.log(a);
console.log("why is this showing up before the getStreamingData function's console output?");
console.log("but it's showing up after the console.log(i) and console.lg(friends[i]) output? So this section is interupting the for loop above");
console.log(" and why is the for loop out of order and repeating itself?");
});//doc ready
You are doing an asynchronous task in your loop. You should not expect those async tasks finish in the order that they have started.
The function getStreamingData is the one that I'm talking about.
Related: Asynchronous for cycle in JavaScript
This is one snippet that I wrote long time ago and I'm still using it in small projects. However there are many libraries out there which do the same plus many more.
Array.prototype.forEachAsync = function (cb, end) {
var _this = this;
setTimeout(function () {
var index = 0;
var next = function () {
if (this.burned) return;
this.burned = true;
index++;
if (index >= _this.length) {
if (end) end();
return;
}
cb(_this[index], next.bind({}));
}
if (_this.length == 0) {
if (end) end();
}else {
cb(_this[0], next.bind({}));
}
}, 0);
}
It is not a good practice to touch the prototype like this. But just to give you an idea how you can do this ...
After that code, you can loop over arrays asynchronously. When you are done with one element, call next.
var array = [1, 2, 3, 4]
array.forEachAsync(function (item, next) {
// do some async task
console.log(item + " started");
setTimeout(function () {
console.log(item + " done");
next();
}, 1000);
}, function () {
console.log("All done!");
});

Why is this javascript object property undefined?

I am using an approach described in detail at Dictionary Lookups in Javascript (see the section"A Client-Side Solution") to create an object that contains a property for each word in the scrabble dictionary.
var dict = {};
//ajax call to read dictionary.txt file
$.get("dictionary.txt", parseResults);
function parseResults(txt) {
var words = txt.split( "\n");
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
console.log(dict.AAH);
console.log(dict);
if (dict.AAH == true) {
console.log('dict.AAH is true!');
}
}
(updated code to use an earlier answer from Phil)
I can't figure out why dict.AAH is returning undefined, but the dict object looks fine in the console. Screenshots from Firebug below.
Console:
Drilled down into "Object { }"
How can I check a given word ("AAH", in this case) and have it return true if it is a property in the dict object defined as true?
Live example
Code on Github
The problem isn't your code. You have invisible characters in your words, which you fail to clean up.
You can verify this by using this as your results parser
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n")
.map($.trim)
.splice(0,3); // Keep only 3 first ones
if(btoa(words[2]) !== btoa('AAH')){ // Compare in Base64
console.log('YOU HAVE HIDDEN CHARS!');
}
}
And you can fix it by whitelisting your characters.
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n").map(function(el){
return el.match(/[a-zA-Z0-9]/g).join('');
});
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
console.log(dict.AAH);
console.log(dict);
if (dict.AAH == true) {
console.log('dict.AAH is true!');
}
}
I would recommend cleaning it up on the server side since running regex on every element in an array as large as seen in your live site might cause performance issues.
It's probably a race condition. You're loading the dictionary in a GET and then immediately (while the request is being made) those console.log commands are being called (and the one comes back undefined). Then the data is actually loaded by the time you debug. Everything should be done in a callback or deferred. It's an understandable quirk of debuggers that's caught me up before.
Get ajax requests are asynchronous. This means that while the whole operation that occurs in the ajax request is going, javascript keeps reading the next lines.
The problem then is you are logging values that the ajax request did not manage to retrieve early enough.
To get around the issue you can include the log calls inside your ajax request callback as below
var dict = {};
//ajax call to read dictionary.txt file
$.get("dictionary.txt", function( txt ){
var words = txt.split( "\n");
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
//Now inside these console.log will run once you DO have the data
console.log(dict.AAH);
console.log(dict);
});
//Stuff out here will run whether or not asynchronous request has finished
I WOULD RECOMMEND USING THE WHEN METHOD IN JQUERY FOR THIS TYPE OF SCENARIOS EVEN MORE AS THE BEST SOLUTION
HERE IS HOW WHAT I THINK WOULD BE MOST PROPER FOR COMPLEX PROJECTS
var dict = {};
//ajax call to read dictionary.txt file
function getDictionary(){
return $.ajax("dictionary.txt");
}
/*I recommend this technique because this will allow you to easily extend your
code to maybe way for more than one ajax request in the future. You can stack
as many asynchronous operations as you want inside the when statement*/
$.when(getDictionary()).then(function(txt){//Added txt here...forgot callback param before
var words = txt.split( "\n");
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
//Now inside these console.log will run once you DO have the data
console.log(dict.AAH);
console.log(dict);
});
You're trying to output dict before it has been populated by the $.get success handler.
Try this:
// If the browser doesn't have String.trim() available, add it...
if (!String.prototype.trim) {
String.prototype.trim=function(){return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');};
String.prototype.ltrim=function(){return this.replace(/^\s+/,'');};
String.prototype.rtrim=function(){return this.replace(/\s+$/,'');};
String.prototype.fulltrim=function(){return this.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ');};
}
/**
* Parses the response returned by the AJAX call
*
* Response parsing logic must be executed only after the
* response has been received. To do so, we have to encapsulate
* it in a function and use it as a onSuccess callback when we
* place our AJAX call.
**/
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n").map($.trim);
for (var i=0; i < words.length; i++){
dict[ words[i] ] = true;
}
console.log(dict.AAH);
console.log(dict);
if (dict.AAH == true) {
console.log('dict.AAH is true!');
}
}
// global object containing retrieved words.
var dict = {};
//ajax call to read dictionary.txt file
$.get("dictionary.txt", parseResults);
As another user commented, jQuery's $.when lets you chain such code.
By the way, if all you want to do is know if a word is in the results you can do:
function parseResults(txt) {
// clean the words when we split the txt
var words = txt.split("\n").map($.trim);
if ($.inArray('AAH', words)) {
console.log('AAH is in the result set');
}
}
I think the problem lays in that you have dict defined as an object but use it as an array.
Replace var dict = {} by var dict = new Array() and your code should work (tried with your live example on Google Chrome).

Categories

Resources