Variables not what I expect in a loop - javascript

I've looked at this questions, but I'm still stuck at this JavaScript closure inside loops – simple practical example
Here's my code
template[prop['Name']] = [];
var processedCounter = -1;
$.each(prop.Properties, function (key, value) {
console.log(processedCounter + 'outside');
$.getJSON(value['Url']).done(function (jsres) {
console.log(processedCounter + 'inside');
var numItems = jsres[itemCount].length;
if (template[prop['Name']].length == 0) {
console.log('this should only be printed once');
for (var i = 0; i < numItems; i++) {
template[prop['Name']].push({});
}
}
processedCounter += 1;
});
});
There's several issues. First it that it prints the message 'this should only be printed once' two times. Second is that the value of processedCounter has to be -1 instead of 0, because the value is increased at the wrong time.

Related

How should I fix this asynchronicity problem in my "Josephus Problem" code?

Background
I'm new to JavaScript and am solving various formulations of the Josephus Problem to better understand the syntax. Using a circularLinkedList implementation*, I've solved the classic formulation: Wikipedia||Numberphile. I've also solved the problem for any fixed number of fighters and a fixed number of skips between eliminations (e.g., if skipping two fighters between eliminations, 1 eliminates 4, 5 eliminates 8, etc). I am now trying to solve the problem given any function that indicates the number of skips at a given moment.
Problem
I can't access the return value of my skip function. I understand from 1, 2, 3 that my issue involves asynchronicity, but am having trouble isolating takeaways from the long responses involving AJAX and jQuery, which I'm unfamiliar with. Could I get an ELI5 solution? I apologize for my lack of understanding.
Code
function winnerStepFunc(num, func) {
let cll = new circularLinkedList(); //Initializing list with participants
for (let a = 0; a < num; a++) {
cll.append(a);
}
function next(funcSteps) { //Generating string indicating #steps from function's output
let toEvaluate = "start";
for (let i = 0; i < funcSteps; i++) {
toEvaluate += ".next"
}
return toEvaluate;
}
let start = cll.getHead(); //Selecting first eliminator
while (cll.size() > 1) {
let toCheck = func(); // PROBLEM LINE
console.log("toCheck = " + toCheck); //toCheck = undefined
let str = next(toCheck);
while (eval(str) !== start && cll.size() > 1) { //
let locCurrent = cll.indexOf(start.element);
start = eval(str).next;
cll.removeAt(((locCurrent + toCheck)% cll.size()));
}
cll.delete(eval(str).next.element);
start = start.next;
}
console.log(start.element + 1);
}
function callFunction(name, args) { // builds function string to be evaluated
let result = name + "(";
for (let i = 0; i < args.length -1; i++) {
result += args[i] + ", ";
}
result += args[args.length-1] + ")";
return result;
}
function callFunction(name) {
let result = `${name}()`;
return result;
}
function addOne() { //<--The first basic example I'm trying:
return ++globalTimes; //Make the step increase by one for each elimination
}
var globalTimes = 0;
winnerStepFunc(12, function(){eval(callFunction("addOne"))});
*CLL Implementation
You don't return in your function. I would remove all the eval stuff and just call the function directly.
winnerStepFunc(12, addOne);

Issue getting closures to work [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I have a piece of code that I'm trying to have alert 1,2,3. I'm having issues using closures properly, so I can't figure this out.
The original code:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
testList();
I am trying to do something like this to buildList() to get it to work properly:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result[i] = function(x) {
result.push( function() {alert(item + ' ' + list[x])} );
}(i);
}
return result;
}
I know I'm making mistakes on working with the closures, I'm just not sure what the problem is.
Your second try was closer to the solution but still doesn't work because your inner-most function is capturing variable item from your top-level function: item is just always referencing the same instance, which was created when calling buildList().
var scope in JavaScript is always bound to current function call, not to code block, so it's not bound to control statements like for.
For that reason, the alerts likely show the value 'item' + (list.length-1) had at the time of calling buildList().
Since you are passing i to your closure, you should declare var item within that function, e.g:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
result[i] = function(x) {
// x and item are both local variables of anonymous function declared just above
var item = 'item' + list[x]; // or maybe you meant 'item' + x?
return function() {alert(item + ' ' + list[x])};
}(i);
}
return result;
}
Note that the closure would still capture a reference to list so will display the value it contains at the time of calling functions in the array returned by buildList(). Also local variable item is completely optional, you could call alert('item' + x /*or is it list[x]?*/ + ' ' + list[x]).
From How do JavaScript closures work?
Note that when you run the example, "item2 undefined" is alerted three
times! This is because just like previous examples, there is only one
closure for the local variables for buildList. When the anonymous
functions are called on the line fnlistj; they all use the same
single closure, and they use the current value for i and item within
that one closure (where i has a value of 3 because the loop had
completed, and item has a value of 'item2'). Note we are indexing from
0 hence item has a value of item2. And the i++ will increment i to the
value 3.
You need to make a closure in each loop iteration if you are to store the matching value of i:
function buildList(list) {
var result = [], item, closure;
for (var i = 0; i < list.length; i++) {
item = 'item' + list[i];
// call this function with the string you wish to store
// the inner function will keep a reference to the 'msg' parameter even after the parent function returns
closure = (function(msg) {
return function() {
alert(msg);
};
}(item + ' ' + list[i]));
result.push( closure );
}
return result;
}
function testList() {
var fnlist = buildList([1, 2, 3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
testList();
Same question asked here and here. Same answers here, here, here, here and probably in dozen more places.

Executing code after calling dynamic number of functions

I'm only crawling in JS so probably the solution is obvious.
I'm writing a Chrome extension which in browser action (after clicking on extension's button) reads several pages and from each of them it retrieves an integer. Then, it creates a table with these integers. I'm doing it qith AJAX so it's asynchronous and I want to have all integers before creating the table.
I've read these topics:
Pass in an array of Deferreds to $.when()
How to tell when multiple functions have completed with jQuery deferred
...and wrote a code that doesn't work.
var sum = 0;
document.addEventListener('DOMContentLoaded', function () {
var deferreds = [];
var users = [...], pages = [...];
for(i = 0; i < users.length; ++i) {
for(j = 0; j < pages.length; ++j)
deferreds.push(window[pages[j]](users[i], pages[j]));
}
$.when.apply(null, deferreds).done(function() {
alert("sum: " + sum);
});
});
function Name(user, page) {
$.get("http://page2/" + user, function(data) {
sum += 7;
});
return 7;
}
function Name2(user, page) {
$.get("http://page/" + user, function(data) {
sum += 84;
});
return 84;
}
So the alert prints 0 instead of 91. I cut the part with table as all fields are "undefined" anyway. If I get sum = 91, I'll probably get the table.
As I said - obvious mistake. The functions should look like:
function Name(user, page) {
return $.get("http://page2/" + user, function(data) {
sum += 7;
});
}

For loop in Javascript runs only once

Here is my code. I do not quite understand why the for loop runs only once, both inner and outer. nodeList.length and innerNodeList.length show appropriate values when I generate alert messages. I see that both i and j do not increment beyond 0. Kindly point out anything wrong with the code.
function getCategoryElements() {
var newCategoryDiv = document.getElementById("category");
var nodeList = newCategoryDiv.childNodes;
for (var i = 0; i < nodeList.length; ++i) {
var innerNodeList = nodeList[i].childNodes;
alert("innerNodeList Length" + innerNodeList.length.toString());
for (var j = 0; j < innerNodeList.length; ++j) {
if (innerNodeList[j].nodeName == "SELECT") {
alert("inside select Node value " + innerNodeList[j].nodeValue.toString());
document.getElementById("newCategories").value =
document.getElementById("newCategories").value + '<%=delimiter%>' + innerNodeList[j].nodeValue;
} else if (innerNodeList[j].nodeName == "TEXTAREA") {
document.getElementById("newCategoriesData").value =
document.getElementById("newCategoriesData").value + '<%=delimiter%>' + innerNodeList[j].nodeValue;
}
}
}
}
var newCategoryDiv, nodeList, innerNodeList, innerNode, i, j;
newCategoryDiv = document.getElementById("category");
nodeList = newCategoryDiv.childNodes;
for (i = 0; i < nodeList.length; ++i) {
innerNodeList = nodeList[i].childNodes;
alert("innerNodeList Length" + innerNodeList.length.toString());
for (j = 0; j < innerNodeList.length; ++j) {
innerNode = innerNodeList[j];
if (innerNode.nodeName === "SELECT") {
alert("inside select Node value " + innerNode.nodeValue.toString());
document.getElementById("newCategories").value += '<%=delimiter%>' + innerNode.nodeValue;
} else if (innerNode.nodeName === "TEXTAREA") {
document.getElementById("newCategoriesData").value += '<%=delimiter%>' + innerNode.nodeValue;
}
// Will this work?
alert('Does this alert appear');
}
}
I took the liberty to refactor your code and clean it up a little bit. In case you're not aware, all variables have function scope in Javascript, so no matter where you declare them within a single function, Javascript treats them as if the variable declaration is the first statement.
It appears that your code is syntactically correct, and so I think that the most logical place to look for a problem is that there could be an error occurring after the last alert function call.
In order to check this, try adding another alert function call to the end of the inner loop. If it doesn't run, you'll know this is the case.

'for loop' and possible syntax error

Here is my current javascript. I know that it worked until I added the 'for loop'. The point of the for loop is to switch letters. As it stands, its checking to see if groups of 3 are equal to one letter. I will fix that later, but I know there must be a syntax error, as the buttons don't fade as my mouse moves over them. I know this is a stupid question and I'll kick myself when I see the error, but I'm tired and can't find it for my life right now. Also, is this for loop a valid way of changing letters? Is that the correct way to set an array value to the new letter?
Here is the script.js:
$(document).ready(function() {
$('#button_translate').mouseenter(function() {
$('#button_translate').fadeTo('fast', 1);
});
$('#button_translate').mouseleave(function() {
$('#button_translate').fadeTo('fast', 0.7);
});
$('#button_clear').mouseenter(function() {
$('#button_clear').fadeTo('fast', 1);
});
$('#button_clear').mouseleave(function() {
$('#button_clear').fadeTo('fast', 0.7);
});
$('#button_translate').click(function() {
var dna = $('input[name=dna]').val();
var dna = dna.toUpperCase();
function allBases(text) {
var bases = /^[ACGT]+$/;
if(text.match(bases)) {
var arr = text.match(/.{1,1}/g);
/*document.write(arr + " is a DNA sequence.");*/
for (var i = 0; i < (dna.length); i++) {
if (arr[i]==='A') {
arr[i]='U'
}else if (arr[i]==='C') {
arr[i]='G'
}else if (arr[i]==='G') {
arr[i]='C'
}else if (arr[i]==='T') {
arr[i]='U'
}else{
document.write(dna + " is not a real DNA sequence. Error Code 2");
}
}
document.write(dna + " is the translated mRNA strand!");
}else{
document.write(dna + " is not a real DNA sequence.");
}
}
allBases(dna);
});
});
for (var i=0; i>=((dna.length) / 3); i++) {
you probably want the loop condition to be i less than dna.length
for (var i = 0; i < (dna.length/3); i++) {
you have an unclosed ( in the for loop
for (var i=0; i>=(dna.length) / 3); i++) { // extra ( in i>=((dna.length) / 3)

Categories

Resources