Javascript for loop ajax potential race condition? - javascript

Here's a simple loop I'm running:
for (var key in TestApp.config.services) {
if (TestApp.config.services[key].files != "") {
var files = TestApp.config.services[key].files.split(',');
for (var i = 0; i <= files.length - 1; i++) {
var file_url = files[i];
console.log("About to download :" + file_url);
$.getJSON('http://whateverorigin.org/get?url=' + encodeURIComponent(file_url) + '&callback=?', function(data) {
console.log("Downloaded file: " + file_url);
console.log(key);
});
}
}
}
The problem is that the key value is always the same by the time the JSON request finishes. How can I avoid this race condition so that the right key value is used when the $.getJSON is finished?

you need an Immediately-invoked function expression (IIFE):
for (var key in TestApp.config.services) {
if (TestApp.config.services[key].files != "") {
var files = TestApp.config.services[key].files.split(',');
for (var i = 0; i <= files.length - 1; i++) {
var file_url = files[i];
console.log("About to download :" + file_url);
// IIFE
(function(thiskey,this_file_url){
$.getJSON('http://whateverorigin.org/get?url=' + encodeURIComponent(this_file_url) + '&callback=?', function(data) {
console.log("Downloaded file: " + this_file_url);
console.log(thiskey);
});
})(key,file_url);
}
}
}

One simple solution is to simply send the key with the request. I prefer to write it in {} notation.
Let me give you the basis to an answer to your question; I will disregard the part with the files, to emphasize where to look at.
index.php
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
var items = ['Hello', 'World', 'foo', 'bar'];
for (var i=0; i < items.length; i++) {
console.log("About to download :" + items[i] + '" - key: ' + i);
$.getJSON( "ajax.php",
{
url: items[i],
key: i
},
function(data) {
var key = data.key;
console.log("Downloaded file: " + data.url + '" - key: ' + key);
}
);
}
</script>
ajax.php
<?php
echo json_encode(array('url'=>$_GET['url'], 'key'=>$_GET['key']));
?>

Related

change API data depending on if statment

I'm trying to fetch data through API , and the URL contains two object and I targeted the quizzes , "quizzes": [2 items], "warnings": []
quizzes return me two objects with their details.
what I'm trying to achieve is to add if statement to retrieve the grades (another API) depends on quiz name and it is working well , but I want to add inside it another if to retrieve grades depends on the another quiz name, please see the code below how to target posttest inside pretest they have the same key and I want the data to be changed depends on quiz name.
var get_quiz = {
"url": "MyURL"
};
$.ajax(get_quiz).done(function (get_quiz_res) {
var reslength = Object.keys(get_quiz_res).length;
for (let b = 0; b < reslength; b++) {
var get_grade = {
"url": "*******&quizid="+get_quiz_res.quizzes[b].id"
};
$.ajax(get_grade).done(function (get_grade_res) {
var posttest=''
if (get_quiz_res.quizzes[b].name === "Post Test"){
posttest = get_grade_res.grade;
}
if (get_quiz_res.quizzes[b].name === "Pre Test"){
var row = $('<tr><td>' + userincourseres[i].fullname + '</td><td>' + get_grade_res.grade + '</td><td>' + posttest + '</td><td>');
$('#myTable').append(row);
}
});
}
});
the userincourseres[i].fullname from another api and it is working.
You can use async/await with $ajax if your JQuery version is 3+.
const get_quiz = {
url: "MyURL",
};
(async function run() {
const get_quiz_res = await $.ajax(get_quiz);
const reslength = Object.keys(get_quiz_res).length;
for (let b = 0; b < reslength; b++) {
const get_grade = {
url: "*******&quizid=" + get_quiz_res.quizzes[b].id,
};
let posttest = "";
const get_grade_res = await $.ajax(get_grade);
if (get_quiz_res.quizzes[b].name === "Post Test") {
posttest = get_grade_res.grade;
}
if (get_quiz_res.quizzes[b].name === "Pre Test") {
var row = $(
"<tr><td>" +
userincourseres[i].fullname +
"</td><td>" +
get_grade_res.grade +
"</td><td>" +
posttest +
"</td><td>"
);
$("#myTable").append(row);
}
}
})();

Variable isn't changing in for loop - JavaScript

Why is this outputting each feedName as the same name (MedryBW)? I've spent a while messing around with it, can't figure it out. I want it to output the name of the feed in each iteration of the loop rather than repeating the same one over and over again. Thanks everyone.
var feeds = ["Towellie", "TrumpSC", "TeamSp00ky", "TwitchPlaysPokemon", "Widgitybear", "AriaBlarg", "TheMexicanRunner", "OPNerd", "rabbitbong", "Wingsofdeath", "MedryBW"];
$(document).ready(function(){
for(x = 0; x < feeds.length; x++){
var feedName = feeds[x];
$.getJSON('https://api.twitch.tv/kraken/streams/' + feeds[x] + '?callback=?', function(data) {
if(data.stream === null){
$('#feeds').append("<p>" + feedName + " is offline </p>");
} else {
$('#feeds').append("<p>" + feedName + " is streaming " (data.stream.game) + "/<p>");
}
});
}
});
Because callback function runs much later, not in the loop, and it just gets variable value after loop has finished (last value), use bind function to pass variable to the function
var feeds = ["Towellie", "TrumpSC", "TeamSp00ky", "TwitchPlaysPokemon", "Widgitybear", "AriaBlarg", "TheMexicanRunner", "OPNerd", "rabbitbong", "Wingsofdeath", "MedryBW"];
$(document).ready(function() {
for(x = 0; x < feeds.length; x++){
var feedName = feeds[x];
$.getJSON('https://api.twitch.tv/kraken/streams/' + feeds[x] + '?callback=?', function(feedName, data) {
if (data.stream === null) {
$('#feeds').append("<p>" + feedName + " is offline </p>");
}else{
$('#feeds').append("<p>" + feedName + " is streaming " + (data.stream.game) + "/<p>");
}
}.bind(this, feedName));
}
});
As you have written Ajax in for loop , The success callback executes after for loop has executed
var feeds = ["Towellie", "TrumpSC", "TeamSp00ky", "TwitchPlaysPokemon", "Widgitybear", "AriaBlarg", "TheMexicanRunner", "OPNerd", "rabbitbong", "Wingsofdeath", "MedryBW"];
$(document).ready(function() {
feeds.forEach(function(feedName) {
$.getJSON('https://api.twitch.tv/kraken/streams/' + feedName + '?callback=?', function(data) {
if (data.stream === null) {
$('#feeds').append("<p>" + feedName + " is offline </p>");
} else {
$('#feeds').append("<p>" + feedName + " is streaming " + (data.stream.game) + "/<p>");
}
});
});
})
THe Above change will work
You can achieve this by.
var feeds = ["Towellie", "TrumpSC", "TeamSp00ky", "TwitchPlaysPokemon", "Widgitybear", "AriaBlarg", "TheMexicanRunner", "OPNerd", "rabbitbong", "Wingsofdeath", "MedryBW"];
$(document).ready(function(){
var divText;
for(x = 0; x < feeds.length; x++){
var feedName = feeds[x];
$.getJSON('https://api.twitch.tv/kraken/streams/' + feeds[x] + '?callback=?', function(data) {
if(data.stream === null){
divText = divText + '<p>'+feedName+' is offline </p>';
}else{
divText = divText + '<p>'+feedName+' is streaming '+(data.stream.game) +'</p>'
}
});
}
$('#feeds').append(divText);
//or $('#feeds').html(divText);
})

Execute script after receiving a response in Chrome extension

I am trying to create a simple chrome extension. When a user clicks a checkbox a few datas are send to the background script and from there to the popup. The data is transmitted successfully and it is stored in localstorage at popup
$(document).ready(function() {
chrome.runtime.sendMessage({
from: 'popup',
subject: 'getmessage',
}, function(response) {
for (var x = 0; x < JSON.parse(response).length; x++) {
localStorage.setItem(JSON.parse(response)[x].acnumber, JSON.stringify(JSON.parse(response)[x]));
}
});
for (var i = 0, len = localStorage.length; i < len; i++) {
var datas = JSON.parse(localStorage.getItem(localStorage.key(i)));
var value = datas;
console.log(value);
$('table').append('<tr class="' + value.acnumber + '"><td>' + value.acnumber + '</td><td>' + value.name + '</td><td>' + value.amount + '</td></tr>')
}
})
The problem is that i have to open the popup twice to see the data (appended to the table). The loop is executing before the datas are added to the localstorage
You'll need to move the append table code inside the response function if you want to process the data right after the response callback is fired. Since you mentioned you need the data on multiple pages, you can move the chrome.runtime.sendMessage in its own function and call it only if you don't already have the data. Something like
function getMessage() {
chrome.runtime.sendMessage({
from: 'popup',
subject: 'getmessage',
}, function(response) {
localStorage['haveMessage'] = true;
for (var x = 0; x < JSON.parse(response).length; x++) {
localStorage.setItem(JSON.parse(response)[x].acnumber, JSON.stringify(JSON.parse(response)[x]));
}
processMessage();
});
}
function processMessage() {
for (var i = 0, len = localStorage.length; i < len; i++) {
var datas = JSON.parse(localStorage.getItem(localStorage.key(i)));
var value = datas;
console.log(value);
$('table').append('<tr class="' + value.acnumber + '"><td>' + value.acnumber + '</td><td>' + value.name + '</td><td>' + value.amount + '</td></tr>')
}
}
if (locaStorage['haveMessage']) {
processMessage();
} else {
getMessage();
}

Using casperjs and phantomjs to scrape multiple pages

I'm trying to scrape a number of pages that have a standard format. I've been able to use Phantomjs to successfully scrape a single page, but when I try to iterate over multiple ones, the asynchronous processing makes things hang up. What's the proper way to tell Casper/Phantom to wait?
var page = require('webpage').create();
var fs = require('fs');
page.onConsoleMessage = function(msg) {
phantom.outputEncoding = "utf-8";
console.log(msg);
};
// this overwrites the previous output file
f = fs.open("lat_long.txt", "w");
f.write("--");
f.close();
// this is the unique identifier for the locations. For now, I just have three datapoints
var EPAID = ["KYD980501076","ME8170022018", "MEN000103584"];
/// this code will be used to loop through the different locations. For now, set to look at only one.
for (q= 0; q < 1; q++) {
var processing = false;
//we construct the target url
var url = "http://iaspub.epa.gov/enviro/efsystemquery.cerclis?fac_search=site_epa_id&fac_value=" + EPAID[0] + "&fac_search_type=Beginning+With&postal_code=&location_address=&add_search_type=Beginning+With&city_name=&county_name=&state_code=&program_search=1&report=2&page_no=1&output_sql_switch=TRUE&database_type=CERCLIS" ;
page.open(url);
page.onLoadFinished = function(status) {
if ( status === "success" ) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
var str = page.evaluate(function() {
$value = [];
$Object = $(".result tr");
for (i =0 ; i < 10; i++) {
$value.push($Object.find('td').html(),$Object.find('td').next().next().html() );
$Object = $Object.next();
}
$string = "{ EPAID: "+ $value[0] + ", " +
"Name: "+ $value[1] + ", " +
"City: "+ $value[4] + ", " +
"State: "+ $value[6] + ", " +
"ZipCode: "+ $value[8] + ", " +
"Latitude: "+ $value[14] + ", " +
"Longitude: "+ $value[16] + " }" ;
return $string;
});
f = fs.open("lat_long.txt", "a");
f.write(str);
f.close();
processing = true;
console.log("writing to file");
phantom.exit();
});
}
// right here it should delay until the previous page is completed
// while (!processing) {
// setTimeout(function(){ console.log("waiting....");},1000);
// }
};
}
console.log("finished all pages");
If you switched to using casperJS, it is as simple as changing your page.open() into page.thenOpen(). (This CasperJS - How to open up all links in an array of links question looks very similar to yours?)
If you wanted to stick with PhantomJS you need to start the next page load in the onSuccess callback of the previous load. This is tedious, and needs care to avoid large memory usage. (I did it once or twice, but now simply use CasperJS.)
An alternative approach is to create the page object inside the loop. However that is not quite answering your question, as then they will run in parallel. But you could use setTimeout to stagger each once to avoid a burst of activity if you have hundreds of URLs!
Here is the code that ultimately works (using the timeout approach since I wasn't able to get the success callback to work better).
With casperjs installed, I named this file "process.js" and was able to run it from the command line as "casperjs process.js"
var page = require('webpage').create();
var fs = require('fs');
page.onConsoleMessage = function(msg) {
phantom.outputEncoding = "utf-8";
console.log(msg);
};
// this overwrites the previous output f
// this is the unique identifier for the locations.
var EPAID = ["NED981713837",... , "FLD049985302", "NJD986643153"];
f = fs.open("lat_long.txt", "w");
f.write("-<>-");
f.close();
var count = 0;
var target = 1400;
var written = [];
function yourFunction(){
if (count < target) {
process(count);
count++;
setTimeout(yourFunction, 5000);
} else {
console.log("exiting");
phantom.exit();
return;
}
}
function process(counter){
var processing = false;
console.log("Beginning record #" + counter);
//we construct the target url
var url = "http://iaspub.epa.gov/enviro/efsystemquery.cerclis?fac_search=site_epa_id&fac_value=" + EPAID[counter] + "&fac_search_type=Beginning+With&postal_code=&location_address=&add_search_type=Beginning+With&city_name=&county_name=&state_code=&program_search=1&report=2&page_no=1&output_sql_switch=TRUE&database_type=CERCLIS" ;
page.open(url);
page.onLoadFinished = function(status) {
if ( status === "success" ) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
var str = page.evaluate(function() {
$value = [];
$Object = $(".result tr");
for (i =0 ; i < 10; i++) {
$value.push($Object.find('td').html(),$Object.find('td').next().next().html() );
$Object = $Object.next();
}
$string = "{ \"EPAID\": \""+ $value[0] + "\", " +
"\"Name\": \""+ $value[1] + "\", " +
"\"City\": \""+ $value[4] + "\", " +
"\"State\": \""+ $value[6] + "\", " +
"\"ZipCode\": \""+ $value[8] + "\", " +
"\"Latitude\": "+ $value[14] + ", " +
"\"Longitude\": "+ $value[16] + " }," ;
return $string;
});
if (written[counter] === undefined) {
f = fs.open("lat_long.txt", "a");
f.write(str);
f.close();
written[counter] = true;
console.log("Writing to file #"+ counter);
}
});
}
};
}
console.log("Start...");
yourFunction();

Javascript Syntax Error "out of range"

I've been doing web development for quite sometime and have never seen this behavior with JavaScript. This is the code I started out with:
function processLogin() {
if (loginReq.readyState == 4) {
var data = eval('(' + loginReq.responseText + ')');
data = data.userData;
var focus = data.team.focus.coordinates;
thisTeam = new Team(data.team.id, data.team.missionId, data.team.name, data.team.operatingArea.coordinates[0]);
if (data.team.zoomLevel != '') {
thisTeam.zoomLevel = data.team.zoomLevel;
}
if (focus.length > 0) {
thisTeam.focusLat = focus[1];
thisTeam.focusLon = focus[0];
}
for (var i = 0; i < data.teams.length; i++) {
var temp_team = new Team(data.teams[i].id, data.teams[i].missionId, data.teams[i].name, []);
teams.push(temp_team);
}
var teamDropDownText = [];
for (var j = 0; j < teams.length; j++) {
if (thisTeam.teamId == teams[j].teamId) {
teamDropDownText.push('<option value="' + teams[j].teamId + '" selected="selected">' + teams[j].name + '</option>');
} else {
teamDropDownText.push('<option value="' + teams[j].teamId + '">' + teams[j].name + '</option>');
}
}
$('#addIncidentTeam').html(teamDropDownText.join(''));
$('#editIncidentTeam').html(teamDropDownText.join(''));
// When all this has finished, make the
// rest of the calls to get the rest of the data
startTimer();
downloadDevices();
initializeMap();
}
}
What I have written there isn't that important, and let me explain why.
The line with the single semicolon after thisTeam.zoomLevel = data.team.zoomLevel; was giving me a syntax error in firebug. I read and re-read my code, and couldn't figure out what I did wrong, so I put the semicolon on the same line as thisTeam.zoomLevel = data.team.zoomLevel and it told me it had a syntax error on the blank line!
To do another test, I moved this whole function to it's own JavaScript file and put everything after that line on one line and even tried to condense some of the code above, so now it looks like this:
function processLogin() {
if (loginReq.readyState == 4) {
var data = eval('(' + loginReq.responseText + ')');
data = data.userData;
var focus = data.team.focus.coordinates;
thisTeam = new Team(data.team.id, data.team.missionId, data.team.name, data.team.operatingArea.coordinates[0]); if (data.team.zoomLevel.length > 0) { thisTeam.zoomLevel = data.team.zoomLevel; } if (focus.length > 0) { thisTeam.focusLat = focus[1];thisTeam.focusLon = focus[0];} for (var i = 0; i < data.teams.length; i++) { var temp_team = new Team(data.teams[i].id, data.teams[i].missionId, data.teams[i].name, []); teams.push(temp_team); } var teamDropDownText = []; for (var j = 0; j < teams.length; j++) { if (thisTeam.teamId == teams[j].teamId) { teamDropDownText.push('<option value="' + teams[j].teamId + '" selected="selected">' + teams[j].name + '</option>'); } else { teamDropDownText.push('<option value="' + teams[j].teamId + '">' + teams[j].name + '</option>'); } } $('#addIncidentTeam').html(teamDropDownText.join('')); $('#editIncidentTeam').html(teamDropDownText.join('')); /* When all this has finished, make the rest of the calls to get the rest of the data */ startTimer(); downloadDevices(); initializeMap(); var kmlLink = document.getElementById('kmlLink'); var geoserverLink = document.getElementById('geoserverLink') if (user_role.substring(0, 1) == 'M') { kmlLink.href = "https://www.intelink.gov/giatstldni/hermes/webservice/kml/download/M&" + thisTeam.missionId + "&48"; kmlLink.innerHTML = "https://www.intelink.gov/giatstldni/hermes/webservice/kml/download/M&" + thisTeam.missionId + "&48"; geoserverLink.href = "https://www.intelink.gov/giatstldni/geoserver/wms/kml?layers=hermes_all&cql_filter=mission_id+=+" + thisTeam.missionId; geoserverLink.innerHTML = "https://www.intelink.gov/giatstldni/geoserver/wms/kml?layers=hermes_all&cql_filter=mission_id+=+" + thisTeam.missionId;} else { kmlLink.href = "https://www.intelink.gov/giatstldni/hermes/webservice/kml/download/T&" + thisTeam.id + "&48"; kmlLink.innerHTML = "https://www.intelink.gov/giatstldni/hermes/webservice/kml/download/T&" + thisTeam.id + "&48"; geoserverLink.href = "https://www.intelink.gov/giatstldni/geoserver/wms/kml?layers=hermes_all&cql_filter=team_id+=+" + thisTeam.id; geoserverLink.innerHTML = "https://www.intelink.gov/giatstldni/geoserver/wms/kml?layers=hermes_all&cql_filter=team_id+=+" + thisTeam.id; } } }
I did this just to see what error I would get, I knew it wouldn't work correctly. But now it's telling me there's an error on a line that doesn't exist in the file! I get:
syntax error
[Break On This Error] (10 out of range 8)
I went and commented more code out and it just made it 10 out of range 6! I don't understand!
I found the culprit. One of the values of the JSON returned was empty (no quotes or anything). Not a very helpful error message.

Categories

Resources