Callback on function call in $.each - javascript

I call a function in an $.each loop, but i want to make sure, that the next iteration is not called until the previous function is done.
$(".btn_loadfavorites").on("click",function(event){
$.getJSON('utility/get_user_compare_pages.php', {uid: user}, function(rsp) {
$.each(rsp, function(i, favorite_pageid) {
loadNewFacebookPage(favorite_pageid);
});
});
});
Here is the function (I stripped it of the unimportant stuff):
function loadNewFacebookPage(newfbpage){
if (isUrl(newfbpage) || newfbpage.substr(0,3) == "www"){
newfbpage = newfbpage.substr(newfbpage.lastIndexOf('/') + 1);
}
$.getJSON( "https://graph.facebook.com/"+newfbpage+"?fields=picture,name,category,likes,talking_about_count,link,website" )
.done(function( rsp ) {
$('#loadmodal').modal('show');
var newpageid = rsp.id;
$("*[data-pcpageid]").each(function(i, elm) {
if(newpageid == $(this).data("pcpageid")){
pageexists = true;
alert('Page has already been added');
return;
}
});
if(pageexists == false){
pagename = rsp.name.split(" ");
pagepicture = rsp.picture.data.url;
$('.grid_fbpages').append("<li class='grid_li_fbpages' data-pcpageid='"+newpageid+"' style='max-width:20%;'><img src='"+pagepicture+"' class='img-circle' style='display:inline;margin-right:15px;'><h4 style='display:inline;'>"+pagename[0]+"</h4><a href='javascript:void(0)' class='btn_removefbpage' style='float:right' data-pcpageid='"+newpageid+"'>&#10005</a> <a href='javascript:void(0)' class='btn_addtofavorites' style='float:right' data-pcpageid='"+newpageid+"'>★</a><hr>Likes: "+rsp.likes+"<br>PTAT: "+rsp.talking_about_count+"<br><br></li>");
//GET POSTS
$.getJSON('utility/get_compare_posts.php', {access_token: access_token, pid: newpageid}, function(rsp) {
$.each(rsp, function(postId, data) {
//TOP POSTS
if (data.hasOwnProperty('likes')){
top_posts_likes.push(data.likes.like_count);
if (data.hasOwnProperty('comments')){
top_posts_comments.push(data.comments.comment_count);
}else{
top_posts_comments.push('0');
}
top_posts_message.push(data.message);
top_posts_id.push(data.postId);
}
});
//TOP POSTS
$(".grid_topposts").append("<li data-pcpageid='"+newpageid+"' style='max-width:20%;text-align:left;'><img src='"+pagepicture+"' class='img-circle' style='display:inline;margin-right:15px;'><h4 style='display:inline;'>"+pagename[0]+"</h4></li>");
most_popular_post_index = top_posts_likes.indexOf(Math.max.apply(Math, top_posts_likes));
$.getJSON( "https://graph.facebook.com/"+top_posts_id[most_popular_post_index]+"?fields=picture&access_token="+access_token+"", function(rsp) {
$(".grid_topposts").append("<li data-pcpageid='"+newpageid+"' style='max-width:20%;'><img src='"+rsp.picture+"'><br>"+top_posts_message[most_popular_post_index]+"<br>Likes: "+top_posts_likes[most_popular_post_index]+" Comments: "+top_posts_comments[most_popular_post_index]+"</li>");
top_posts_likes.splice(most_popular_post_index,1);
top_posts_message.splice(most_popular_post_index,1);
top_posts_id.splice(most_popular_post_index,1);
top_posts_comments.splice(most_popular_post_index,1);
most_popular_post_index = top_posts_likes.indexOf(Math.max.apply(Math, top_posts_likes));
$.getJSON( "https://graph.facebook.com/"+top_posts_id[most_popular_post_index]+"?fields=picture&access_token="+access_token+"", function(rsp) {
$(".grid_topposts").append("<li data-pcpageid='"+newpageid+"' style='max-width:20%;'><img src='"+rsp.picture+"'><br>"+top_posts_message[most_popular_post_index]+"<br>Likes: "+top_posts_likes[most_popular_post_index]+" Comments: "+top_posts_comments[most_popular_post_index]+"</li>");
top_posts_likes.splice(most_popular_post_index,1);
top_posts_message.splice(most_popular_post_index,1);
top_posts_id.splice(most_popular_post_index,1);
top_posts_comments.splice(most_popular_post_index,1);
most_popular_post_index = top_posts_likes.indexOf(Math.max.apply(Math, top_posts_likes));
$.getJSON( "https://graph.facebook.com/"+top_posts_id[most_popular_post_index]+"?fields=picture&access_token="+access_token+"", function(rsp) {
$(".grid_topposts").append("<li data-pcpageid='"+newpageid+"' style='max-width:20%;'><img src='"+rsp.picture+"'><br>"+top_posts_message[most_popular_post_index]+"<br>Likes: "+top_posts_likes[most_popular_post_index]+" Comments: "+top_posts_comments[most_popular_post_index]+"</li>");
top_posts_likes.splice(most_popular_post_index,1);
top_posts_message.splice(most_popular_post_index,1);
top_posts_id.splice(most_popular_post_index,1);
top_posts_comments.splice(most_popular_post_index,1);
});
});
});
//END TOP POSTS
});
}
})
.fail(function( error ) {
alert('Did not find any match - Please try again with another name, ID or URL');
});
}
Thanks

$.each() is synchronous. It's just a loop that calls a function directly, nothing fancy or mysterious about it. In your code, $.each() will call loadNewFacebookPage(), that function will run to completion, and then $.each() will move on to the next element.
But is that what you're really asking about? loadNewFacebookPage() sounds like it's probably an asynchronous function. If it is, are you saying that you want to wait until the asynchronous activity has completed before moving on to the next loop iteration? The $.each() loop won't do that. Nor would an ordinary for loop. Instead, loadNewFacebookPage() would need to provide a completion callback, and that callback would advance a "loop" variable.
If you can tell more about loadNewFacebookPage()—in particular whether it has a completion callback or one could be added—then it would be more clear what to do.
For example, suppose loadNewFacebookPage() takes a second argument which is a completion callback function. Then you could write code like this:
$.getJSON( url, { uid: user }, function( rsp ) {
var i = 0;
next();
function next() {
if( i < rsp.length ) {
var favorite_pageid = rsp[i++];
loadNewFacebookPage( favorite_pageid, next );
}
}
});
Suppose that loadNewFacebookPage() were using the $.ajax() function to load the data. (It probably isn't doing that, is it? But this will serve to illustrate.) Then you might do something like:
function loadNewFacebookPage( id, done ) {
$.ajax({
url: makeUrlFromId( id ),
success: function( data ) {
doStuffWithData( data );
done();
}
});
}
I'm omitting a bunch of stuff here like error handling just to illustrate an example.

Related

How retrieve results after asynchronous function?

I've read posts on asynchronous calls and callbacks but i did not find my answer.
i've a simple dropdown button on my boostrap page, who let users change values.
So when button is clicked, i call an ajax function to do the job, and we must wait for result of the action.
After that, i want to change dropdown button value and give the result of the operation to the user.
So this is my callback function with ajax (which works great) :
function changePermissions(user, role, callBack){
var ret = false;
$.ajax({
url: "lib/edit_permissions.php",
data: {'user': user, 'role': role},
type: 'post',
dataType: 'json',
success: function(data) {
if($.isPlainObject(data) && data.state === 200){
ret = true;
}
else {
console.log(data.message);
}
return callBack( ret );
},
error: function (xhr, ajaxOptions, thrownError) {
console.log("thrown error");
return callBack( ret );
}
});
}
If i try to retrieve the result outside the function, the value is undefined, i understand that it is an asynchronous problem:
$(".dropdown-menu li a").click(function(){
var newrole = $(this).text();
var result=false;
result = changePermissions(user, newrole, function(ret) {
return ret;
});
}
if (result){
$(this).parents(".dropdown").find('.btn').html(newrole + ' <span class="caret"></span>');
console.log("permissions successfully updated.");
}
else {
console.log("Error to update permissions");
}
If i try to retrieve the result inside the function, the result is OK. But i can't update my button text value, '$this' seems to be undefined:
$(".dropdown-menu li a").click(function(){
var newrole = $(this).text();
var user = $(this).parents(".dropdown").closest('td').prev('td').text();
changePermissions(user, newrole, function(ret) {
if (ret){
$(this).parents(".dropdown").find('.btn').html(newrole + ' <span class="caret"></span>');
console.log("user permissions successfully updated.");
}
else{
console.log("error to update");
}
});
});
How can i do that ?
That is normal because, this inside callback doesn't refers to DOM object or target of the event. It overrides. To use it, you need to create a local copy of that and to use that copy inside callback. Here is code.
$(".dropdown-menu li a").click(function(){
var newrole = $(this).text();
var user = $(this).parents(".dropdown").closest('td').prev('td').text();
//make copy of this
var self = this;
changePermissions(user, newrole, function(ret) {
if (ret){
//use that copy
$(self).parents(".dropdown").find('.btn').html(newrole + ' <span class="caret"></span>');
console.log("user permissions successfully updated.");
}
else{
console.log("error to update");
}
});
});

My $(document).ajaxComplete() doesn't work

I'm messing with jQuery an AJAX for few days and I've come to a dead end. I'm trying to load some content with AJAX -this part works just fine - and after that I want to execute other script. However, my content loaded with first script doesn't show up till the second script is finished. Code looks like this:
$.ajax({
url : folder,
success: function (data) {
$(data).find("a").attr("href", function (i, val) {
if( val.match(/\.(jpe?g|png|gif)$/) ) {
var $codeText = "<li class='ui-state-default'><a href='#' data-featherlight='" + folder + val + "'><img src='" + thumbFolder + val + "' data-src='"+ thumbFolder + val + "'/></a></li>";
$("ul[id=sortable]").append($codeText);
$("li").featherlight(folder + val);
};
});
}
})
$(window).ajaxComplete(function(){
console.log("Haba");
var x = 0;
while( x < 50000 )
{
console.log("Haba nr "+x);
x++;
}
The whole code is triggered, but images are showing up on website after all the console messages. Does anyone have any suggestion what to do to show up pictures first? I've also tried with done(). and ajaxStop(), still no effect.
you have to use the .done() function of ajax to get the asynchrone result :
var jqxhr = $.ajax( "example.php" )
.done(function(data) {
alert( "success : " + data );
})
.fail(function() {
alert( "error" );
})
.always(function() {
alert( "complete" );
});
Why not just move the code you want to run after the pictures are loaded into the success function of the ajax call and do that after the code to load pictures?
$.ajax({
url : folder,
success: function (data) {
//Code to load pictures
console.log("Haba");
var x = 0;
while( x < 50000 ) {
console.log("Haba nr "+x);
x++;
}
});
I think the reason it's not behaving the way you want it to is because the ajax call is complete after it gets a response back. That response triggers the ajax complete function that runs before your logic in the success function of the ajax call. Also, the way you have it written, the ajax complete logic will fire even if your ajax call fails. Which I don't think you want.
try to use setTimeout()
function ajaxDone(){
console.log("Haba");
var x = 0;
while( x < 50000 )
{
console.log("Haba nr "+x);
x++;
}
}
$(window).ajaxComplete(function(){
window.setTimeout(ajaxDone,1);
});

How to print JavaScript based on Ajax request?

Allow me to elaborate.
I have this Ajax script which is fetching for one thing. The refresh_on.
What does it do? It either returns 0 OR 1.
function startRefresh() {
setTimeout(startRefresh, 60000);
$.ajax({
url: 'refresh.php',
type: 'POST',
dataType: 'JSON',
data: {task: "reload"},
success: function(data) {
$.each(data, function(i, attr){
if (attr.refresh_on == 0) {
//this doesn't work
/*Write/return this in JavaScript:*/ line[1]="Offline.";
} else {
//this doesn't work
/*Write/return this in JavaScript:*/ line[1]="Online.";
};
})
}
});
}
If the ajax returns with refresh_on == 0 OR refresh_on == 1 - I want it to print its respective array item. It must be an array item.
</head>
<body>
<script type="text/javascript">
var line=new Array()
startRefresh();
//output either "line[1]=\"Offline.\""; or "line[1]=\"Online.\"";
</script>
</body>
This is the PHP file:
if (isset($_POST['task']) && $_POST['task'] == "reload") {
$stmt = $connection->prepare("SELECT refresh_on FROM refresh");
$stmt->execute();
$result = $stmt->get_result();
$encode = Array();
while ($row = $result->fetch_assoc()) {
$encode[] = $row;
}
echo json_encode($encode);
}
If it matters - this is the JSON response:
[{"refresh_on":1}]
Is there a way to insert/output/print the array item using the function?
Any help would be appreciated.
So there's a couple things that might be going on here, but to begin, you're wrapping your variable in a quote, so all that is happening is a string is being made and immediately being dropped to the floor. Let's start off by doing something like the following:
function startRefresh() {
setTimeout(startRefresh, 60000);
$.ajax({
url: 'refresh.php',
type: 'POST',
dataType: 'JSON',
data: {task: "reload"},
success: function(data) {
$.each(data, function(i, attr){
if (attr.refresh_on == 0) {
//this doesn't work
line[1] = "Offline.";
} else {
//this doesn't work
line[1]= "Online.";
};
})
}
});
}
Now, if I recall correctly, the ajax function is going to be calling your success callback, which is going to take over your local scope. I don't believe line is going to be accessible to that callback, so we should/could actually move that callback to its own function within a closure:
<script type="text/javascript">
(function() {
var line = [];
startRefresh();
function startRefresh() {
setTimeout(startRefresh, 60000);
$.ajax({
url: 'refresh.php',
type: 'POST',
dataType: 'JSON',
data: { task: "reload" },
success: refreshResponse
});
}
function refreshResponse(data) {
$.each(data, function(i, attr) {
if (attr.refresh_on == 0) {
line[1] = "Offline.";
} else {
line[1] = "Online.";
}
});
}
})();
</script>
We've wrapped that in a self executing function to give us some nice encapsulation to work with, and because line is in a function which refreshResponse is found, that variable should be accessible to it.
But we're not done yet!
For one, we could easily make that variable assignment a little easier, like so:
line[1] = (attr.refresh_on == 0) ? "Offline." : "Online.";
...and we're also going to want to triple up on that equality statement, just to avoid variable coercion:
line[1] = (attr.refresh_on === 0) ? "Offline." : "Online.";
Give that a shot and let's see where we're at.
Not sure what you are expecting "line[1]=\"Offline.\""; to do, but it's not going to do anything. Perhaps you mean: line[1]= "Offline";? Try putting this line of code there instead to test that it's working: console.log('Offline');
If the line is getting executed and you see the output in your console, you would just have to target some HTML element that you want to put the "Offline" string into, for instance:
<div id="status"></div>
<script>
....
var status = document.getElementById("status");
if (attr.refresh_on == 0) {
status.textContent = "Offline";
} else {
status.textContent = "Online";
};
....
</script>

Javascript Returns Incorrect Value

I have a function which returns data from an AJAX call to determine if a user has any credits remaining and then displays the data accordingly. The strange thing is that the function does not return the expected result. Here is the code:
function setupBuildInputs() {
noCreditsError = '<div class="alert alert-danger">You have no build credits remaining, please contact a representative to purchase more.</div>';
if ( $('#newWebsiteBuildForm #user_id').length ) {
$('#newWebsiteBuildForm #user_id').change(function () {
userID = $(this).val();
jQuery.get(SITE_URL + '/ajax.php?action=getBuildCreditsRemaining&id=' + userID, function(numOfCredits) {
if ( numOfCredits > 0 ) {
// Get latest website URL and show it on the page
$('#websiteURLHolder').html('Updating...');
jQuery.get(SITE_URL + '/ajax.php?action=getNextWebsiteUsername&id=' + userID, function(data) {
$('#websiteURLHolder').html(data + '.checkoutyournewsite.com');
});
} else {
$('#quickBuildTagsHolder').html( noCreditsError );
}
});
});
}
$('#newWebsiteBuildForm #template').change(function () {
// Show the build form items
numOfRemainingCredits = checkBuildCredits( $('#newWebsiteBuildForm #user_id').val() );
alert(numOfRemainingCredits);
if ( numOfRemainingCredits > 0 ) {
$('#quickBuildTagsHolder').html('Updating...');
jQuery.get(SITE_URL + '/ajax.php?action=returnQuickBuildTags&template=' + $(this).val(), function(data) {
$('#quickBuildTagsHolder').html(data);
});
} else {
$('#quickBuildTagsHolder').html( noCreditsError );
}
});
}
function checkBuildCredits( userID ) {
buildCredits = 0;
jQuery.get(SITE_URL + '/ajax.php?action=getBuildCreditsRemaining&id=' + userID, function(data) {
buildCredits = data;
});
return buildCredits;
}
setupBuildInputs();
Using firebug I cans see that the call to ajax.php?action=getBuildCreditsRemaining pulls the correct id from the page and returns the correct value (9999). To debug further I added the alert on the second change event handler and the result is coming back as 0 instead of 9999.
Next I added 2 alerts to the even added an checkBuildCredits function. The first verified that the ajax call works and that data is set as 9999. The second strangely shows that buildCredits is still set to 0 right before the function returns.
In the setupBuildInputs function, the first change handler uses the same ajax call and it works fine, the second change handler which uses the functions of course fails since it doesn't get 9999 and instead sees 0.
Any ideas what is happening here?
replace checkBuildCredits with this
function checkBuildCredits( userID, successFn ) {
jQuery.get(SITE_URL + '/ajax.php?action=getBuildCreditsRemaining&id=' + userID, function(data) {
successFn(data);
});
}
Then when you call checkBuildCredits, do it like this
checkBuildCredits( $('#newWebsiteBuildForm #user_id').val(), function(numOfRemainingCredits )
{
if ( numOfRemainingCredits > 0 ) {
$('#quickBuildTagsHolder').html('Updating...');
jQuery.get(SITE_URL + '/ajax.php?action=returnQuickBuildTags&template=' + $(this).val(), function(data) {
$('#quickBuildTagsHolder').html(data);
});
} else {
$('#quickBuildTagsHolder').html( noCreditsError );
}
});
As the others have explained it, jquery.get is Asynchronous. And based on your design, you are treating jquery.get as a Synchronous call.
jquery.get is defaultly Asynchronous. so before your fetch the data,the function return the 0 value in your function checkBuildCredits
Instead of $.get() try using the following:
$.ajax({
url: SITE_URL + '/ajax.php?action=getBuildCreditsRemaining&id=' + userID,
success: function(data) {
buildCredits = data;
},
asynch: false
});
This will wait for the ajax call to complete before continuing script execution.

Advice requested - passing variables between functions using json/jquery & ajax

I've looked over a lot of 'similar' q&a threads on SO but to be honest, as I don't have too much of a grip on js programming, I'm finding it difficult to make sense of a lot of the answers (as far as they may apply to my own situation).
The context is this, I have two php scripts one returning a list of customer_ids (json encoded) for a set period and the other returning their preferences for news feeds (json encoded).
I wrote the following, having googled a bit to get a basic understanding of how to setup an ajax function in jQuery:
$('document').ready(function() {
$.ajax({
type:'GET', url: 'cust_selection.php', data: '',
succes:function(cstmrid) {
var clistlen = cstmrid.length;
var i=0;
var cstmr;
for( ;cstmr=cstmrid[i++]; ) {
$('#adminPanel>ul>li').append("<a href='' onclick='alert("+cstmr+")' class='lst_admin basic'>"+cstmr+"</a>"); //alert to be replaced with a function call which passes customerid to the function below.
}
},
dataType:'json'
});
var cstmrid = "483972258"; //hardcoded for testing purposes
$.ajax({
type:'GET', url:'newsfpref.php?', data:'cref='+cstmrid,
success:function(npfdata) {
var item;
var n=0;
for( ;item=npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>"+item+"</td></tr>");
}
},
dataType:'json'
});
});
Now from the first ajax function, I get a list of links which I want to be able to click to launch the second ajax function and pass it the customer id so that it can grab a list of the news sources that they've configured for their pages.
The alert and the hard-coded customer id both suggest that the functions are 'working', but when I try and adjust the first function so that:
...
$('#adminPanel>ul>li').append("<a href='' onclick='getCustomerNP("+cstmr+")' class='lst_admin basic'>"+cstmr+"</a>");
... is calling a modified version of the second function, as below:
...
function getCustomerNP(cstmrid) {
$.ajax({
type:'GET', url:'newsfpref.php?', data:'cref='+cstmrid,
success:function(nprfdata) {
var item;
var n=0;
for( ;item=npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>"+item+"</td></tr>");
}
},
dataType:'json'
});
}
Everything seems to just fail at this point. The second function doesn't seem to 'receive' the variable and I'm not sure if it's something elementary that I've overlooked (like some muddled up " and ' placements) or if what I am trying to accomplish is actually not the way jQuery ajax functions interact with each other.
As you can see, I've cannibalised bits of code and ideas from many SO q&a threads, but copying without much of an understanding makes for a frustratingly dependent life.
I would appreciate as much - expansive - comment as you can provide, as well as a solution or two (naturally).
EDIT: Not to confuse anyone further, I've been modifying the above and correcting my (many) errors and typos along the way. At present, the code looks like below:
$('document').ready(function () {
$.ajax({
type: 'GET', url: 'cust_selection.php', data: '',
succes: function (cstmrid) {
var clistlen = cstmrid.length;
var i = 0;
var cstmr;
for (; cstmr = cstmrid[i++]; ) {
var a = $("<a href='' class='lst_admin basic'>" + cstmr + "</a>").click(function () {
getCustomerNP(cstmr)
})
$('#adminPanel>ul>li').append(a); //alert to be replaced with a function call which passes customerid to the function below.
}
},
dataType: 'json'
});
function getCustomerNP(cstmr) {
alert(cstmr);
}
});
You've got a typo in the $.ajax() success function within getCustomerNP(). The function declaration:
success:function(nprfdata) {
... has a parameter nprfdata, but then within the function you use npfdata (missing the r).
Also this code:
var item;
var n=0;
for( ;item=npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>"+item+"</td></tr>");
}
...declares and sets variable news that you never use. And it doesn't seem right to increment n in the for test expression but then use n within the loop. You never set item to anything but you use it in your .append().
(Note also that JS doesn't have block scope, only function scope, so declaring variables inside an if or for loop doesn't limit them to that if or for block.)
I would not create inline onclick handlers like that. I'd probably do something more like this:
$('#adminPanel>ul>li').append("<a href='' data-cstmr='"+cstmr+"' class='lst_admin basic'>"+cstmr+"</a>");
...and then within the document ready setup a delegated event handler to catch the clicks on those anchors:
$('#adminPanel>ul>li').on('click', 'a.lst_admin', function() {
$.ajax({
type:'GET', url:'newsfpref.php?', data:'cref='+ $(this).attr('data-cstmr'),
success:function(npfdata) {
var item,
n=0,
// cache the jQuery object rather than reselecting on every iteration
$table = $('#adminMain>table>tbody');
// increment n only after the current iteration of the loop
for( ;item=npfdata[n]; n++) {
// change to use item
$table.append("<tr><td>"+item.nsource+"</td></tr>");
}
},
dataType:'json'
});
});
As you append your like with <a href='' onclick='getCustomerNP("+cstmr+")', Make sure you can access the function getCustomerNP.
Try to define getCustomerNP as
window.getCustomerNP = function(cstmrid) {
...
If you defined it in the $(document).ready(function(){ ... }) block, try this
$('document').ready(function () {
$.ajax({
type: 'GET', url: 'cust_selection.php', data: '',
succes: function (cstmrid) {
var clistlen = cstmrid.length;
var i = 0;
var cstmr;
for (; cstmr = cstmrid[i++]; ) {
var a = $("<a href='' class='lst_admin basic'>" + cstmr + "</a>").click(function () {
getCustomerNP(cstmr)
})
$('#adminPanel>ul>li').append(a); //alert to be replaced with a function call which passes customerid to the function below.
}
},
dataType: 'json'
});
function getCustomerNP(cstmrid) {
$.ajax({
type: 'GET', url: 'newsfpref.php?', data: 'cref=' + cstmrid,
success: function (nprfdata) {
var item;
var n = 0;
for (; item = npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>" + item + "</td></tr>");
}
},
dataType: 'json'
});
}
});

Categories

Resources