This question already has answers here:
How do you make javascript code execute *in order*
(9 answers)
Closed 3 years ago.
First off, I am new to JS, HTML, and CSS.
When I debug this code, it seems to not run in the order I would expect, top to bottom. What am I doing wrong?
This code is supposed to use the twitch.tv api to find the channels I follow, take their ID, run another ajax call to see if they are live, and if they are live, present them on my html page.
I have tried debugging this and running ajax calls in Postman and the calls do work for the Twitch API and I get the information I want. The problem is, it runs out of order so the code doesnt do what I intend. I tried debugging with Chrome break points and Debugger for Chrome extension in VS Code.
$(document).ready(function() {
var userID = [];
var logo = [];
var status = [];
var name = [];
var totalFollowing;
//Get User IDs for following
$.ajax({
type: 'GET',
url: 'https://api.twitch.tv/kraken/users/Lucidic_/follows/channels?limit=100',
headers: {
'Client-ID': 'hidden',
'Accept': 'application/vnd.twitch.v5+json'
},
success: function(followingData) {
for (var i = 0; i < followingData._total; i++) {
totalFollowing = followingData._total;
userID.push(followingData.follows[i].channel._id);
logo.push(followingData.follows[i].channel.logo);
status.push(followingData.follows[i].channel.status);
name.push(followingData.follows[i].channel.display_name);
}
}
});
var allFollowingURL = "https://api.twitch.tv/helix/streams?";
for (var i = 0; i < totalFollowing; i++) {
allFollowingURL.concat("&user_id=" + userID[i])
}
$.ajax({
type: "GET",
url: allFollowingURL,
headers: {
'Client-ID': 'hidden',
'Accept': 'application/vnd.twitch.v5+json'
},
success: function(channelData) {
if (channelData.data.type = 'live') {
$("#followerInfo").prepend("<div class = 'row'>" + "<div class = 'col-sm-4'>" + "<img src='" + logo[i] + "'>" + "</div>" + "<div class = 'col-sm-4'>" + name[i] + "</div>" + "<div class = 'col-sm-4'>" + status[i] + "</div></div>");
}
}
});
});
The reason for the code "running out of order" is because the API requests you are making run asynchronously. These requests take time to return with the data, so instead of holding your program hostage while it waits for this request to come back, the program keeps on executing, then goes back to execute the code in the request's call back function once the data has returned.
See more on learning about something called a promise here https://developers.google.com/web/fundamentals/primers/promises. These are very powerful and useful for handling things like http requests.
Related
Let me explain: My purpose is to create moodle users from a web app.
I am implementing a web app on Tomcat 8.0.15.0. That is, I use java servlets on the server side. Many JSP files and javascript, with much of it in jQuery, resides on the client side.
On the other hand, on another server, I have a test moodle installation. Via site-administration> plugins> web services> external services, I created a ws that enables the core_user_create_users function. Also created a token to access this ws, and put the admin user as authorized user.
And then, typed the following URL on Chrome:
https://mysyte.com/webservice/rest/server.php?wstoken=780f8b3a1164163d4dc00a757071194e&wsfunction=core_user_create_users&moodlewsrestformat=json&users[0][username]=testuser&usr[ [email] =john#smith.com&users [0] [password] = XXXXXX
And it worked. It returned a blank page, with the text
[{"id": 1, "username": "testuser"}]
Thus creating a user in moodle.
My question is: How can I do this from java?, or from javascript?, or from jQuery even better.
And if not, from PHP, I guess I would have no problem calling it from java, or javascript, or jQuery.
My Wrong Hint: In another part of the application I used, in javascript, the call $.getJSON() successfully. That's why I thought would also serve me in this case. But no success now, when the mozilla debugger reaches the call, it hangs.
Any feedback will be most welcome.
The call looks like
function create_moodle_user(username,firstname,lastname,email,password) {
var url = "https://mysyte.com/webservice/rest/server.php?"
+ "wstoken=780f8b3a1164163d4dc00a757071194e" + "&"
+ "wsfunction=core_user_create_users" + "&"
+ "moodlewsrestformat=json" + "&"
+ "users[0][username]=" + username + "&"
+ "users[0][firstname]=" + firstname + "&"
+ "users[0][lastname]=" + lastname + "&"
+ "users[0][email]=" + email + "&"
+ "users[0][password]=" + password;
$.getJSON(url, function(data) { // should return [{"id":4,"username":"testuser"}]
// this point is never reached
if (data.length < 64) {
}
else {
}
});
}
Finally, it worked by changing the call and the way how parameters were passed.
function create_moodle_user(u,f,l,e,fn) {
var domainname = 'https://my.moodle.site.com';
var data = {
wstoken: '780f8b3a1164163d4dc00a757071194e'
wsfunction: 'core_user_create_users'
moodlewsrestformat: 'json',
users: [
{
username:u,
password:'xxxxxxxx',
email:e,
firstname:f,
lastname:l
}
]
};
var response = $.ajax({
type: 'GET',
data: data,
url: domainname + '/webservice/rest/server.php'
});
// pass the function parameter
response.done(fn);
}
And this worked!
My problem now is to get user info, since I don't know how to get the response from core_user_get_users_by_field.
This is what I have:
function get_moodle_user(u,fn) {
var domainname = 'https://my.moodle.site.com';
var data = {
wstoken: '780f8b3a1164163d4dc00a757071194e'
wsfunction: 'core_user_get_users_by_field'
moodlewsrestformat: 'json',
field: 'username',
username:u
};
var response = $.ajax({
type: 'GET',
data: data,
url: domainname + '/webservice/rest/server.php'
});
console.log(response); // this does not show the result data
// pass the function parameter
response.done(fn);
}
Any ideas, please?
I have been using an AJAX query in my JavaScript code, and want my HTML & JavaScript code to pause loading until the results of the query are in. Is this possible? I believe async functions continue executing your code but make it possible to 'callback' to the function once it has completed a step. In my case I just want everything to stop, so nothng happening asynchronously.
For an example, please see my code below.
function ajaxQuerySpotify(stringIncrementalAmount) {
$.ajax({
url: 'https://api.spotify.com/v1/me/tracks?limit=50&offset=' + stringIncrementalAmount,
headers: {
'Authorization': 'Bearer ' + access_token
},
success: function(response) {
for (i = 0; i < response['items'].length; i++) {
songIDs.push(response['items'][i]['track']['id']);
songNames.push(response['items'][i]['track']['album']['artists'][0]['name']);
SongArtists.push(response['items'][i]['track']['name']);
}
requestCount += 1;
incrementalRequest = requestCount * incrementalAmount;
stringIncrementalAmount = String(incrementalRequest);
// console.log(stringIncrementalAmount);
// SET BACK TO 1000 ONCE COMPLETED TESTING
if (incrementalRequest >= 999) {
return false;
}
// Keeps requerying until nothing can be found anymore
ajaxQuerySpotify(stringIncrementalAmount);
}
})
}
I have the following function:
function updateproductselectionxxx(form, productassignment, mainproductid, subprodqty) {
var checkingurl = "shopajaxproductselection.asp";
var pars = 'productassignment=' + productassignment + '&qty=' + subprodqty + '&mainid=' + mainproductid;
var url = checkingurl + '?' + pars;
var target = 'productselectionresult' + productassignment;
var myAjax = new Ajax.Updater(target, checkingurl, {
method: 'post',
parameters: pars
});
}
And I am currently in the process of converting all the javascript on this website to jQuery. Usually I can use something similar to:
function updateproductselection(form, productassignment, mainproductid, subprodqty) {
$.ajax({
type: 'POST',
url: 'shopajaxproductselection.asp',
data: $(form).serialize(),
success: function (response) {
$(form).find('productselectionresult' + productassignment).html(response);
}
});
return false;
}
And that does the trick, however I really only want to send over 1 field as indicated in the first function and I would also like to send along the information I am sending directly to the function upon it being called. JavaScript is definitely not my area of expertise but usually I can muddle through, but this time everything I have tried has caused errors and I'm not getting very far. Any help would be greatly appreciated.
Looks like a bit of confusion between POST and GET. Although the request method is set to POST in the older Prototype version the params are being sent via CGI which normally appear on the server as a GET. It's a bit hard to say more without seeing the server-side code, but you could try this, such that the jQuery version more closely mimics the old Prototype version:
function updateproductselection(form, productassignment, mainproductid, subprodqty) {
var checkingurl = "shopajaxproductselection.asp";
var pars = 'productassignment=' + productassignment + '&qty=' + subprodqty + '&mainid=' + mainproductid;
var url = checkingurl + '?' + pars;
$.ajax({
type: 'POST',
url: url,
data: {},
success: function (response) {
$(form).find('#productselectionresult' + productassignment).html(response);
}
});
return false;
}
Note that I have added a hash # to the start of productselectionresult - this is crucial due to the difference in the way PrototypeJS works. In Prototype, you can use an ID selector like:
$('id')
whereas in jQuery it has to be:
$('#id')
I have javascript/jquery code which fetches info and updates it into the database with a mixture of while/for loops. While fetching, I have a div which shows a current progress log of whats going on. In Firefox, as the script is running it updates the div at the same time as it should. In Google Chrome, it runs the entire loop, holding back the log, and only outputs it until the script is finished running. Anyone have any idea why this is happening?
Here is my code:
$(document).ready(function() {
add_text("test");
var array_length = num_sets;
for(var i = 0; i < array_length; i = i + 1) {
var setId = sets[i]['id'];
var setName = sets[i]['name'];
var setLinkName = sets[i]['link'];
var setNumCards = sets[i]['num_cards'];
add_text("Beginning to fetch set \"" + setName + "\"");
add_text("Found " + setNumCards + " total cards.");
while(ii < setNumCards) {
var card_name = sets[i]['cards'][ii]['name'];
var card_link = sets[i]['cards'][ii]['link'];
add_text("Fetching card " + sets[i]['cards'][ii]['name']);
fetch_card(sets[i]['cards'][ii]['link'], setId);
}
}
});
add_text function:
function add_text(text) {
$("#status_text").append("<br />" + text);
}
fetch_card function:
function fetch_card(card_link, set_id)
{
$.ajax({
url: "fetch_card.php?link=" + card_link + "&set_id=" + set_id,
context: document.body,
async: false,
success: function(){
ii = ii + 1;
}
});
}
You are using synchronous ajax calls (which are generally not very desirable). The browser can block all activity until that ajax call completes. Whether or not the browser updates the screen during a synchronous ajax call is up to the browser.
Your code would be much better if it was rewritten to use asychronous ajax only. It takes a little more work to structure your code properly to work with asynchronous ajax calls, but the browser remains completely responsive during the asynchronous ajax calls.
I'm not entirely sure how you were using the ii variable in your original implementation (as it wasn't declared or initialized in the code you included), but this is the general structure you could use. It uses the traditional for loop to collect all the data you wanted in an array, then calls the ajax function one a time on that data. It isn't clear to me how you're actually doing anything with the returned ajax info, but perhaps that just isn't something you included here:
$(document).ready(function() {
add_text("test");
var array_length = num_sets;
var fetchData = [];
var fetchIndex = 0;
for(var i = 0; i < array_length; i++) {
var setId = sets[i]['id'];
var setName = sets[i]['name'];
var setLinkName = sets[i]['link'];
var setNumCards = sets[i]['num_cards'];
add_text("Beginning to fetch set \"" + setName + "\"");
add_text("Found " + setNumCards + " total cards.");
for (var ii = 0; ii < setNumCards; ii++) {
var card_name = sets[i]['cards'][ii]['name'];
var card_link = sets[i]['cards'][ii]['link'];
add_text("Fetching card " + sets[i]['cards'][ii]['name']);
fetchData.push({link: sets[i]['cards'][ii]['link'], id: setId});
}
}
function next() {
if (fetchIndex < fetchData.length) {
fetch_card(fetchData[fetchIndex].link, fetchData[fetchIndex].id, next);
fetchIndex++;
}
}
function fetch_card(card_link, set_id, successFn) {
$.ajax({
url: "fetch_card.php?link=" + card_link + "&set_id=" + set_id,
context: document.body,
async: true,
success: successFn
});
}
next();
});
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
AJAX- response data not saved to global scope?
I basically have a loop that contacts a script on my site using AJAX and then updates a string with the response from that script. Here's the code:
// Image code array
var result_url = 'http://localhost/view/';
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
$.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
});
}
console.log(result_url);
The Firebug console just shows http://localhost/view/ when the string is logged. It's like the img_code response from my upload script isn't being appended to the string at all. I have tried logging the value of result_url within the $.post() method and that works fine, but the value is not being saved properly because it doesn't show later in my code. Is this a scope problem? Will I have to define result_url as a global variable?
Thanks for any help.
You are checking console.log(result_url); before the AJAX requests complete.
AJAX requests are (by default) run asynchronously. What that means is that your script continues to run while the request is still being made to the server.
Your callback function (provided to $.post as the 3rd parameter) is the one that get's executed after your AJAX request has completed.
Also note, that your AJAX request callback functions are called when the request is done. Your requests might not finish in the same order that they started. You could prevent all this by setting async:false, but that'll halt all of your javascript execution.
Another option would be to collect the jqXHR objects being returned by $.post, and then call $.when().done(), so that your console.log(result_url) happens only when all the AJAX requests are resolved:
// Image code array
var result_url = 'http://localhost/view/',
jqHXRs = [];
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
jqHXRs.push($.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
}));
}
$.when.apply(this, jqHXRs).done(function(){
console.log(result_url);
});
This is because you're doing the console.log immediately after firing the Ajax. Since Ajax is asynchronous, the success function will not necessarily be called before the code which follows your ajax code.
jQuery's Ajax tools provide a way of calling ajax synchronously by including the async:false option. Try replacing your ajax call with:
$.ajax({
url:'/upload',
data:{ url: rls[i], username:username },
success:function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
},
method:"post",
async:false
});
That way, code which follows you Ajax call would only be executed after the ajax completes.
Remember, though that this will lock up your page for the duration of the Ajax. Maybe it would be easier to just put the console.log(result_url); at the end of the success callback.
You're logging the result_url after the loop, but the $.post upload request may not have been complete yet. What I would recommend is to put the code that uses result_url inside a continuation call back and call it after you know that the last post request has completed.
e.g.
function continuation_code(result_url) {
// all your code that uses result_url goes here.
}
var result_url = 'http://localhost/view/';
var num_results_returned = 0;
for(i = 0; i < urls.length; i++) {
// Add URL to queue
$('#url_queue').append('<div class="uploadifyQueueItem"><div class="cancel"><img src="/assets/img/cancel.png" /></div><span class="fileName">' + image_name_from_url(urls[i]) + '</span><div class="uploadifyProgress"><div class="uploadifyProgressBar"></div></div></div>');
// Make a request to the upload script
$.post('/upload', { url: urls[i], username: username }, function(response) {
var response = jQuery.parseJSON(response);
if(response.error) {
alert(response.error);
return;
}
if(response.img_code) {
result_url += response.img_code + '&';
}
num_results_returned += 1;
if (num_results_returned == urls.length) {
continuation_code(result_url);
}
});
}