I have a problem with my output of this. getYammerJSON contains a line that also appends. The issue is that when I run the script it puts all of the appends that are not inside the function first, and then the contents of the function, is it something to do with ready?
<div id="test"></div>
_
$(function(){
while(n<len){
$('#test').append("<br/><br/>Yammer Group ID: <b>" + groupIDs[n] + "<b/><br/><br/>");
getYammerJSON(page,n)
n++;
}
});
function getData(returnData){
$.each(returnData.users, function(key, value){
if(value.email != undefined){
$('#test').append(value.email + "<br/>");
}
});
}
function getYammerJSON(page,n){
$.get("https://www.yammer.com/api/v1/users/in_group/" + groupIDs[n] + ".json?page=" + page, function (returnData) {
getData(returnData);
if(!returnData.more_available === true){
return false;
}
else {
page++;
getYammerJSON(page,n);
}
});
}
an example (it should separate the emails by group ID, not put at the top) -
Yammer Group ID: 12069
Yammer Group ID: 46371
adetan#test.com
alanild#test.com
alexchi#test.com
alisoc#test.com
alwyn#test.com
...
This is a asynchronous problem. You expect the functions to fire after each other. They don't. The jQuery get is asynchronous. So it fires when the request is ready, not when the function is completed. This causes data to appear in the wrong place. You need to rethink the way #test is being populated.
The solution here will solve the asynchronous problem.
function getYammerJSON(page,n){
if (!$('#yg_'+n)[0]) //test if the div already exists, if not create.
{
$('#test').append("<div id='yg_"+n+"'>Yammer Group ID: <b>" + groupIDs[n] + "<b/></div><br/>");
}
$.get("https://www.yammer.com/api/v1/users/in_group/" + groupIDs[n] + ".json?page=" + page, function (returnData) {
getData(returnData, n);
if(!returnData.more_available === true){
return false;
}
else {
page++;
getYammerJSON(page,n);
}
});
}
function getData(returnData, n){
$.each(returnData.users, function(key, value){
if(value.email != undefined){
$('#yg_'+n).append(value.email + "<br/>");
}
});
Now everytime getYammerJSON is called it will create a new div inside #test if it doesn't already exists. The div is given an ID that refers to the n value. When getData is called it will append the addresses into the correct div always corresponding with the correct group id.
asynchronous forces you to rethink your strategy of data retrieval. It's relying more on callbacks. There are new techniques around the corner. They are mentioned in comments, but they are far from common practice. Till that day you need to apply call backs in order to make asynchronous work without faults.
Learn more here: Asynchronous vs synchronous execution, what does it really mean?
Related
I am trying to find out if a text file (note) exists on the server.
I want to return "yes" if it does, "no" if it does not.
I am able to get this into an alert successfully (for each of the 7 occurrences) but I want to return it back into the originating html doc so I can use it in future code. I am reading about callbacks on this site, but not sure how to implement it.
I tried adding some callback code to the success function, that I saw in an example elsewhere here but am unsure how to edit my function call:
tmpNoteFileSun is a text string that matches the format of the text files stored on the server.
The function call (there are 7 of these in separate places, 1 for each day of the week):
CheckNoteExist(tmpNoteFileSun);
var DoesTheNoteExist = ""; //code needs to go here that returns noteexists (as in the alert below).
I tried changing the above to:
var DoesTheNoteExist = CheckNoteExist(tmpNoteFileSun);
console.log("Does Note Exist " + DoesTheNoteExist);
But get undefined in the console.
The Ajax Function:
function CheckNoteExist(ThisNoteName, callback) {
var NoteFileName = ThisNoteName;
// Ajax to call an external php file, pass the notes filename to it and check if the file
// exists. If it does, change noteexists variable to Yes", else it is "no".
$.ajax({
url: 'ajaxfile_note_exists.php',
type: 'GET',
data: {NoteFileName: NoteFileName},
success: function(noteexists) {
alert("Does the note exist: " + noteexists);
callback && callback(noteexists);
}
});
}
The external PHP file:
$filename = "upload/" . $_GET['NoteFileName'];
if (file_exists($filename)) {
$noteexists = "yes";
}
else {
$noteexists = "no";
}
echo $noteexists;
?>
You're not using the callback, that's what it's there for.
CheckNoteExist(ThisNoteName, val => console.log("Does Not Exist " + val));
See also How do I return the response from an asynchronous call? and Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
What is wrong here:
function deluser() {
var id = $('.nameact').attr('data-id');
$.post('users-pro-del.php', {id}, function(data) {
$('.nameact').remove();
$('.namesingle').eq(0).addClass('nameact');
});
}
users-pro-del.php - deletes a user from database.
So I want to remove the corresponding item from screen
$('.nameact').remove();
And give the active status to another (the first) item:
$('.namesingle').eq(0).addClass('nameact');
Sometimes it works fine.
But more often - $('.namesingle').eq(0) is also removed !
As I can see in console - there is only one single .nameact at each moment.
Any help?
Ajax has asynchronous nature. id = $('.nameact') before $.post may be differ from the $('.nameact') after ajax request. It's better to refer nameact by Id everywhere.
function deluser() {
var id = $('.nameact').attr('data-id');
console.log('id to delete: ' + id);
$.post('users-pro-del.php', {id}, function(data) {
$('.nameact[data-id="' + id + '"]').remove(); // to be sure we delete the same object in php and JS
console.log('deleted id: ' + id);
$('.namesingle').eq(0).addClass('nameact');
});
}
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!");
});
I am trying to make an external AJAX call to an API within a Jquery each loop.
Here is the code I have so far.
getStylesInfo(tmpMake, tmpModel, tmpModelYear, tmpSubmodel).done(function(data){
var holder = [];
$.each(styles, function(index, value) {
var tempValue = value;
var temp = getNavigationInfo(value.id);
$.when(temp).done(function(){
if(arguments[0].equipmentCount == 1){
holder.push(tempValue);
console.log(holder);
}
});
});
});
console.log(holder);
function getStylesInfo(make, model, year, submodel){
return $.ajax({
type: "GET",
url: apiUrlBase + make + '/' + model + '/' + year + '/' + 'styles? fmt=json&' + 'submodel=' + submodel + '&api_key=' + edmundsApiKey + '&view=full',
dataType: "jsonp"
});
function getNavigationInfo(styleId){
return $.ajax({
type: "GET",
url: apiUrlBase + 'styles/' + styleId + '/equipment?availability=standard&name=NAVIGATION_SYSTEM&fmt=json&api_key=' + edmundsApiKey,
dataType: "jsonp"
});
The getStylesInfo() returns something similar to this. An array of objects with info about a car model.
var sampleReturnedData = [{'drivenWheels': 'front wheel drive', 'id': 234321}, {'drivenWheels': 'front wheel drive', 'id': 994301}, {'drivenWheels': 'rear wheel drive', 'id': 032021}, {'drivenWheels': 'all wheel drive', 'id': 184555}];
I am trying to loop through the sampleReturnedData and use each id as a parameter in a different AJAX call with the getNavigationInfo() function.
I want to loop through the results and make a check. If it is true then I want to push the entire object to the holder array.
The problem is the console.log(holder) outside the function returns an empty array. The console.log(holder) within the if statement works properly.
I am not sure if this is a scope/hoisting issue or a problem with the way I am using deferreds?
I have read this question and many like it. They suggest to use either
async:false
Or to rewrite the code better. I have tried and used console debugger numerous times. I don't want to set it to false. I'm just unsure what exactly is going on.
I've also read up on hoisting via this article.
I believe it has to do with deferreds but I don't have enough JS knowledge to figure it out.
Thanks!
I am not sure if this is a scope/hoisting issue or a problem with the way I am using deferreds?
In fact, it is both:
holder is declared only within the callback function (as a local variable), so it's undefined outside the function.
And the console.log is executed before the asynchronous callback function does fill the array with values, so even if holder was in scope it would still have been empty. See also Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
So what you should do is indeed to rewrite your code to use promises properly :-)
getStylesInfo(tmpMake, tmpModel, tmpModelYear, tmpSubmodel).then(function(data) {
var holder = [];
var promises = $.map(data.styles, function(value, index) {
return getNavigationInfo(value.id).then(function(v){
if (v.equipmentCount == 1)
holder.push(value);
});
});
return $.when.apply($, promises).then(function() {
return holder;
}); // a promise for the `holder` array when all navigation requests are done
}).then(function(holder) {
console.log(holder); // use the array here, in an async callback
});
I am using d3.js to draw several bars. After the drawing is done, I want to change the color of selected bars, but it always fails. It seems drawBars() and updateColor() are called at the same time.
function redraw(data){
drawBars(data);
updateColor();
}
How can I ensure drawBars() is done before the updateColor() is called?
At first, I added the update function code at the end of drawBar, it didn't work. Later, I moved it to end of the redraw function, it didn't work either.
This is specific code:
function drawBar(drawData){
var w = 1060,h = 600;
d3.selectAll("svg").remove();
var svg = d3.select("#chart").append("svg").attr("width",w).attr("height",h);
uni.forEach(function(element,index,array){
$('.uni').css("top",function(index){
return (index + 1) * 18 + 28;
});
});
$(".rankingheader").css("display","block");
$("#switch input").prop("checked", false);
starData = drawData;
revCountData = drawData;
sentData = drawData;
bizCountData = drawData;
drawAxis(drawData);
drawStar(starData);
drawRev(revCountData);
drawSent(sentData);
drawBiz(bizCountData);
drawLineA(starData);
drawLineB(starData,revCountData);
drawLineC(revCountData,sentData);
drawLineD(sentData,bizCountData);
// cart is a list which stored the id selected by user
if(cart.length > 0){
cart.forEach(function(element,index,array){
d3.select("#starHoverLine" + element).attr("visibility","visible");
d3.select("#starHintText" + element).attr("visibility","visible");
d3.select("#revHoverLine" + element).attr("visibility","visible");
d3.select("#revHintText" + element).attr("visibility","visible");
d3.select("#sentHoverLine" + element).attr("visibility","visible");
d3.select("#sentHintText" + element).attr("visibility","visible");
d3.select("#bizHoverLine" + element).attr("visibility","visible");
d3.select("#bizHintText" + element).attr("visibility","visible");
d3.select("#lineA" + element).style("stroke","red");
d3.select("#lineB" + element).style("stroke","red");
d3.select("#lineC" + element).style("stroke","red");
d3.select("#lineD" + element).style("stroke","red");
d3.select("#starBar" + element).attr("fill","red");
d3.select("#revBar" + element).attr("fill","red");
d3.select("#sentBar" + element).attr("fill","red");
d3.select("#bizBar" + element).attr("fill","red");
});
}
}
Your two functions are not called simultaneously; they're called one after another. If it appears that they're being called simultaneously, one of two things is happening:
drawBars is starting something that finishes asynchronously (like an ajax request or an animation), or
drawBars is making changes that aren't shown by the browser immediately and you want to yield back to the browser briefly to allow it to show (render) those changes before calling updateColor.
If it's #1, then you'll need to look at the documentation for the asynchronous operation that drawBars is starting to find out how to know when it finishes. Usually this is a callback or a promise. Depending on which it is, you'll either need to pass updateColors into drawBars as an argument (so you can then call it from the asynchronous callback), or have drawBars return the promise and use the promise's then method to add updateColors to the queue of functions to call when the promise is fulfilled.
If it's #2, that's much simpler:
setTimeout(updateColors, 100); // Wait 100ms (1/10th second) and call `updateColors`