In my application, I have an XML file that connects to different API and gathers information. Then in a div, it shows the fetched information.
I do it successfully, but the issue is, the XML will not load if element one by one. It loads data in parallel, so the divs it creates are not in the right order like how they are in the xml file.
Here is a demo that get some data from local, SoundCloud, Spotify and iTunes.
And this is the XML file it is loading from:
$.ajax({
url: "https://dl.dropboxusercontent.com/u/33538012/text.xml",
dataType: "xml",
success: parse,
error: function() {
alert("Error");
}
});
function parse(document) {
var inc = 1;
$(document).find("track").each(function() {
var rowNum = inc;
var trackType = $(this).find('type').text();
var trackTitle = $(this).find('title').text();
var soundcloud_url = $(this).find('soundcloud_url').text();
var spotify_track_uri = $(this).find('spotify_track_uri').text();
var itunes_id = $(this).find('itunes_id').text();
if (trackType == "audio") {
inc = inc + 1;
$(".playlist").append("<div id='" + rowNum + "' >" + rowNum + " " + trackTitle + "</div>");
} else if (trackType == "soundcloud") {
inc = inc + 1;
SC.initialize({
client_id: "b8f06bbb8e4e9e201f9e6e46001c3acb"
});
SC.resolve(soundcloud_url).then(function(sound) {
trackTitle = sound.title;
$(".playlist").append("<div id='" + rowNum + "' >" + rowNum + " " + trackTitle + "</div>");
});
} else if (trackType == "spotify") {
inc = inc + 1;
var spotifyLink = "https://api.spotify.com/v1/tracks/" + spotify_track_uri;
$.getJSON(spotifyLink, function(data) {
trackTitle = data.name;
$(".playlist").append("<div id='" + rowNum + "' >" + rowNum + " " + trackTitle + "</div>");
});
} else if (trackType == "itunes") {
inc = inc + 1;
var iTuensLink = "https://itunes.apple.com/lookup?id=" + itunes_id + "&callback=?";
$.getJSON(iTuensLink, function(data) {
trackTitle = data.results[0].trackName;
$(".playlist").append("<div id='" + rowNum + "' >" + rowNum + " " + trackTitle + "</div>");
})
}
});
}
<script src="https://connect.soundcloud.com/sdk/sdk-3.1.2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="playlist"></div>
If you look at the result of the code, the the number next to each row shows the order in the original XML file, but they are not loaded exactly one after each other.
Any way to sort the loaded files just like how they are written in the XML file?
You can use deffered object which are an extension of promise and can resolve it self.So they can act as a trigger when used with resolve
Crate an array that would hold our deffered objects note that order is important therefore numeric array is used
Just create a deffered
var dfd = jQuery.Deferred();
Define what should be done on
dfd.then(function(){
$(".playlist").append("<div id='" + rowNum + "' >" + rowNum + " " + trackTitle + "</div>");
})
Finally push it on our array
x.push(dfd)
After all api calls are over just call resolve on each
$.each(x, function(){
this.resolve(); //then callback gets called one by one on each deffered
});
https://jsfiddle.net/sanddune/rr7tord6/
You might want to have some mechanism to detect when the slowest call finishes and then call resolve
EDIT:
The latency of individual api varies greatly across geographies for example for me soundcloud was the most "laggy" so it ended up last but for someone else itunes may be the last to finish this is due to many variables like distance from server,ISP,throttling etc. so clearly you can never rely on "last man standing " approach so i could only suggest that use some sort of counter like this to track how many request are outstanding
First get total no. of nodes (inn xml) that have an api involved say you have just soundcloud and shopify so now total = 2 ; count = 0
so when the api request yields increment it by one like
$.getJSON(spotifyLink, function(data) {
trackTitle = data.name;
alert('shopify loaded');
count++;check();
});
or
SC.resolve(soundcloud_url).then(function(sound) {
trackTitle = sound.title;
alert('soundcloud loaded');
count++;check();
});
note that you need to increment on even failed api request (which may result from bad parameter etc.)
call a check function that will check if count == total on every success/fail part of api request
function check(){
if(count==total)
alert('all api request finished');
//resolve all deffereds
}
Related
I am facing a problem where I want to return all the rows one by one without interupting the current function. The issue is intermittent. Sometimes I can get all the datatables data but sometimes not.
Then I started investigating and realised after checking the developer tool in network section that it throws error after page 10. And the error is 429 too many requests. Also, realised that the script was synchronous json call.
429 too many requests
I tried using below code in my script but the spin is not working. Also, found that this is not recommended way.
Can someone help me with solution? Thank you
// Set the global configs to synchronous
$.ajaxSetup({
async: false
});
My Script
function getMed(sDate, eDate) {
var pData;
var firstURL = mUrl + "?sDate=" + moment(sDate).format(dateFormat) + "&eDate=" + moment(eDate).add(1, 'day').format(dateFormat) + "&pNum=1";
....
}).done(function (data) {
if (pData.Number > 0) {
var counter = 0;
// Set the global configs to synchronous
$.ajaxSetup({
async: false
});
var requestsProcessed = 0;
for (var i = 1; i <= pData.Number; i++) {
$("#iconSpin").css('display', 'block');
var pURL = mUrl + "?sDate=" + moment(sDate).format(dateFormat) + "&eDate=" + moment(eDate).add(1, 'day').format(dateFormat) + "&pNum=" + i;
console.log("calling: " + pURL);
var pageRequest = $.getJSON(pURL, function (data) {
requestsProcessed++;
$("#progress").innerHTML = "Fetching batch " + requestsProcessed + " of " + pData.Number + " batches.";
....
}).fail(function () {
$("#iconSpin").css('display', 'none');
console.log("fail for " + pURL + " in " + new Date());
});
console.log("completed for " + pURL + " in " + new Date());
}
} else {
alert("There is no data.");
}
}).fail(function () {
$("#iconSpin").css('display', 'none');
});
}
I'm looking for a way to send many requests to an api using a different api url each time.
An example url for my project is:
http://api.bandsintown.com/artists/Hippo%20Campus/events.json?lapi_version=2.0&app_id=music_matcher
I'm using an HTTP request to pull the JSON info into my script and works perfectly...the first time. However, I want to be able to call it 50-100 ish times (max) in a loop with different artist names in the url (I'm using the BandsInTown API). For some reason, when I try to use a loop to call the http request multiple times, only one output appears and it is unpredictable which element in the order it will be (it's usually the output associated with the first or second element in the array). This is what my code looks like:
// HTTP GET call to BandsInTown API
function httpGetAsync(theUrl, callback) { //theURL or a path to file
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
var data = JSON.parse(httpRequest.responseText);
if (callback) {
callback(data);
}
}
else {
alert("error loading JSON doc");
}
};
httpRequest.open('GET', theUrl, true);
httpRequest.send(null);
}
//extracts data from api for each artist
function parseEvent(artist) {
var url = "http://api.bandsintown.com/artists/" + artist + "/events.json?lapi_version=2.0&app_id=music_matcher";
httpGetAsync(url, function(data) {
var numEvents = Object.keys(data).length;
//var events = [];
for (var j = 0; j < numEvents; j++) {
document.write(data[j].venue.name + "-> ");
document.write("LAT:" + data[j].venue.latitude + " " + "LNG:" + data[j].venue.longitude);
document.write("ARTIST: " + data[j].artists[0].name);
document.write("DATE: " + data[j].datetime);
document.write(" " + j + " ");
}
});
}
var artists = ["Drake", "Mac Demarco", "Hippo Campus", "STRFKR"];
for (var i = 0; i < artists.length; i++) {
parseEvent(artists[i]);
document.write(" ---NEXT ARTIST--- ");
}
So I can't tell exactly what's going on but things are acting weird with my current code. I don't have a whole lot of javascript and web development experience yet so any help is appreciated! I was preferably looking for a way to implement this with pure javascript. I have had trouble figureing out how to handle Node.js and/or JQuery in Eclipse Neon (the IDE I am using)
You have implemented closure pretty well so clearly this isn't a problem of success callback of one function overwriting response of all others.But now when you look at document.write() it all gets clear, this function first wipes your whole content clean then it writes whatever you told it to .That's why you hardly see anyone use it
`document.write('a');`
`document.write('b');`
`document.write('c');` // a and b are gone you would see only 'c'
So after loop gets over you would only see the output of the last call.Though it's mostly random as to which call would finish last it mostly biased towards some particular value due to the the way servers are tuned.
So better approach is to use some <div> or something and pour your results into it like this one
<div id="op"></div>
and
function parseEvent(artist) {
var url = "http://api.bandsintown.com/artists/" + artist + "/events.json?lapi_version=2.0&app_id=music_matcher";
httpGetAsync(url, function(data) {
var numEvents = Object.keys(data).length;
var op = document.getElementById('op');
op.innerHTML = op.innerHTML + " <br><br> <h2>---NEXT ARTIST---<h2> <br>";
//var events = [];
for (var j = 0; j < numEvents; j++) {
op.innerHTML = op.innerHTML + "<br>" + data[j].venue.name + "-> ";
op.innerHTML = op.innerHTML + "<br>" + "LAT:" + data[j].venue.latitude + " " + "LNG:" + data[j].venue.longitude ;
op.innerHTML = op.innerHTML + "<br>" +"ARTIST: " + data[j].artists[0].name;
op.innerHTML = op.innerHTML + "<br>" +"DATE: " + data[j].datetime;
op.innerHTML = op.innerHTML + "<br>" + " " + j + " <br>";
}
});
}
var artists = ["Drake", "Hippo Campus", "STRFKR","Mac Demarco"];
for (var i = 0; i < artists.length; i++) {
parseEvent(artists[i]);
}
I am trying to create a webapp that will display information and update it when a JSON file is updated. My current code only updates when both the JSON file are updated and the page is refreshed. The key is not to have the page refreshed. Here is my current code
<script>
$.getJSON('package.json', function (data) {
for (var i in data) {
var username = data[i].username;
var value = data[i].value;
var tokens = data[i].tokens;
$("#playerlist").append('<tr><td>-' + username + ' has deposited $' + value + ' in ' + tokens + ' tokens' + '</td></tr>');
}
});
</script>
Any help is very appreciated. Thanks!
By using SetInterval we can update the UI in some timeinterval.
function refreshContent()
{
$.getJSON('package.json', function (data) {
for (var i in data) {
var username = data[i].username;
var value = data[i].value;
var tokens = data[i].tokens;
$("#playerlist").append('<tr><td>-' + username + ' has deposited $' + value + ' in ' + tokens + ' tokens' + '</td></tr>');
}
setTimeOut(refreshContent, 1000) /// again wait for 1 sec and do refresh
});
}
setTimeOut(refreshContent, 1000) \\\this will update in every 1 sec.
I've been working on a script which collates the scores for a list of user from a website. One problem is though, I'm trying to load the next page in the while loop, but the function is not being loaded...
casper.then(function () {
var fs = require('fs');
json = require('usernames.json');
var length = json.username.length;
leaderboard = {};
for (var ii = 0; ii < length; ii++) {
var currentName = json.username[ii];
this.thenOpen("http://www.url.com?ul=" + currentName + "&sortdir=desc&sort=lastfound", function (id) {
return function () {
this.capture("Screenshots/" + json.username[id] + ".png");
if (!casper.exists(x("//*[contains(text(), 'That username does not exist in the system')]"))) {
if (casper.exists(x('//*[#id="ctl00_ContentBody_ResultsPanel"]/table[2]'))) {
this.thenEvaluate(tgsagc.tagNextLink);
tgsagc.cacheCount = 0;
tgsagc.
continue = true;
this.echo("------------ " + json.username[id] + " ------------");
while (tgsagc.
continue) {
this.then(function () {
this.evaluate(tgsagc.tagNextLink);
var findDates, pageNumber;
pageNumber = this.evaluate(tgsagc.pageNumber);
findDates = this.evaluate(tgsagc.getFindDates);
this.echo("Found " + findDates.length + " on page " + pageNumber);
tgsagc.checkFinds(findDates);
this.echo(tgsagc.cacheCount + " Caches for " + json.username[id]);
this.echo("Continue? " + tgsagc["continue"]);
this.click("#tgsagc-link-next");
});
}
leaderboard[json.username[id]] = tgsagc.cacheCount;
console.log("Final Count: " + leaderboard[json.username[id]]);
console.log(JSON.stringify(leaderboard));
} else {
this.echo("------------ " + json.username[id] + " ------------");
this.echo("0 Caches Found");
leaderboard[json.username[id]] = 0;
console.log(JSON.stringify(leaderboard));
}
} else {
this.echo("------------ " + json.username[id] + " ------------");
this.echo("No User found with that Username");
leaderboard[json.username[id]] = null;
console.log(JSON.stringify(leaderboard));
}
});
while (tgsagc.continue) {
this.then(function(){
this.evaluate(tgsagc.tagNextLink);
var findDates, pageNumber;
pageNumber = this.evaluate(tgsagc.pageNumber);
findDates = this.evaluate(tgsagc.getFindDates);
this.echo("Found " + findDates.length + " on page " + pageNumber);
tgsagc.checkFinds(findDates);
this.echo(tgsagc.cacheCount + " Caches for " + json.username[id]);
this.echo("Continue? " + tgsagc["continue"]);
return this.click("#tgsagc-link-next");
});
}
Ok, looking at this code I can suggest a couple of changes you should make:
I don't think you should be calling return from within your function within then(). This maybe terminating the function prematurely. Looking at the casperjs documentation, the examples don't return anything either.
Within your while loop, what sets "tgsagc.continue" to false?
Don't use "continue" as a variable name. It is a reserved word in Javascript used for terminating an iteration of a loop. In your case this shouldn't be a problem, but its bad practice anyhow.
Don't continually re-define the method within your call to the then() function. Refactor your code so that it is defined once elsewhere.
We ended up having to scope the function, so it loads the next page in the loop.
This is mainly because CasperJS is not designed to calculate scores, and it tries to asynchronously do the calculation, missing the required functions
Sorry about the odd question wording. In all honesty I'm in right at the deep end here, having to learn a huge amount all at once.
I'm trying to figure out how to perform an asynchronous search on a database using AJAX, but it keeps coming up with a "500 internal server error". I've done some AJAX tutorials but none of them involve the MVC method of web development. I'm a bit lost with what information needs to be sent where, and how.
All I know is that a 500 Server Error means it's happening on the server side rather than the client side, so I presume there's a break in logic at the point where the controller starts to get involved. That's just a newbie guess though.
I'll paste all of what I believe to be the relevant code. For context, the database info is from an 'accounts' table in a mock bank database I've been working with for practice.
Thanks for any help.
Firstly, here's the error information I get when looking at debug info on Chrome
Now here's the code involved.
Javascript/JQuery:
var $j = jQuery.noConflict();
var key = 0;
$j(function () {
$j("#search_btn").click(function () {
key = $j("#acc-id-search").val();
searchAcc();
return false;
})
});
function searchAcc() {
var callback = function (data) {
$j("#account_data_table").empty();
var htmlArray = [];
if (data.total > 0) {
$j.each(data.items, function (i, item) {
htmlArray.push("<tr>");
htmlArray.push('<td class="text-center">' + item.account_id + '</td>');
htmlArray.push('<td class="text-center">' + item.product_id + '</td>');
htmlArray.push('<td class="text-center">' + item.cust_id + '</td>');
htmlArray.push('<td class="text-center">' + item.open_date + '</td>');
htmlArray.push('<td class="text-center">' + item.close_date + '</td>');
htmlArray.push('<td class="text-center">' + item.last_activity_date + '</td>');
htmlArray.push('<td class="text-center">' + item.status + '</td>');
htmlArray.push('<td class="text-center">' + item.open_branch_id + '</td>');
htmlArray.push('<td class="text-center">' + item.open_emp_id + '</td>');
htmlArray.push('<td class="text-center">' + item.avail_balance + '</td>');
htmlArray.push('<td class="text-center">' + item.pending_balance + '</td>');
htmlArray.push("</tr>");
});
}
else {
htmlArray.push('<tr><td colspan="10">No results!</td></tr>');
}
$j("#account_data_table").append(htmlArray.join(''));
alert("Sucess?");
};
alert("Searching for '" + key + "'");
postData('#Url.Content("~/Accounts/Index")', key, callback, '#account_data_table');
}
function postData(url, data, callback, lockObj, dataType, loadingMessage)
{
data = data || {};
dataType = dataType || 'json';
loadingMessage = loadingMessage || 'Loading...';;
var isLock = !!lockObj;
$.ajax({
url: url,
data: data,
method: 'post',
dataType: dataType,
cache: false,
beforeSend: function(){
alert("About to send");
},
success: callback,
error: function(){
alert("Failed..!");
},
complete: function(){
}
});
}
The controller that '#Url.Content("~/Accounts/Index")' points to:
[HttpPost]
public NewtonJsonResult Index(int key)
{
var _service = new BankEntities();
var searchCondition = new account() { account_id = key };
var resultObj = new AjaxDataResult();
var allitems = _service.All(searchCondition);
var itemArray = new JArray();
resultObj.total = allitems.Count();
JObject temp;
foreach(var item in allitems)
{
temp = new JObject();
temp["account_id"] = item.account_id;
temp["product_cd"] = item.product_cd;
temp["cust_id"] = item.cust_id;
temp["open_date"] = item.open_date;
temp["close_date"] = item.close_date;
temp["last_activity_date"] = item.last_activity_date;
temp["status"] = item.status;
temp["open_branch_id"] = item.open_branch_id;
temp["open_emp_id"] = item.open_emp_id;
temp["avail_balance"] = item.avail_balance;
temp["pending_balance"] = item.pending_balance;
itemArray.Add(temp);
}
resultObj.items = itemArray;
return new NewtonJsonResult(resultObj);
}
The 'All(searchcondition)' method which is used to find the required items in the table:
public List<account> All(account acc)
{
var data = accounts.Where(x => x.status == "ACTIVE");
if(acc.account_id != 0)
{
data = data.Where(x => x.account_id == acc.account_id);
}
return data.OrderBy(x => x.account_id).ToList();
}
Special thanks to #nnnnnn whose answer lies in the comments after the initial question:
The method might not be called because you're not setting the right
request parameters, so no method exists for the request hence the 500
error. Your current code is trying to include the key value without
giving it a request parameter name. Try changing key =
$j("#acc-id-search").val(); to key = { key: $j("#acc-id-search").val()
};. (The actual request would then end up with a parameter
key=thesearchvaluehere.)