Executing code after calling dynamic number of functions - javascript

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;
});
}

Related

This Javascript For Loop (based on length of an array) Doesn't Run When The Variables Exist?

I have some nested for loops.
The 'm' loop doesn't seem to run - that is to say that the code contained within it doesn't execute.
The problem could be based around the fact that I'm using the length of an array that derives from a JSON array... but the code I use shows that the length of the array is a number - so should be fine!
I'm stumped. I have included console logs all the way to isolate the issue - it just seems that the 'm' loop doesn't fire...
function stuff() {
var obj = [];
var textpasted = document.getElementById('textpasted').value;
var vocabpasted = document.getElementById('vocabpasted').value;
var newtextpasted = textpasted.split(" ");
var newvocabpasted = vocabpasted.split(" ");
var i,
j,
m,
olength;
for (i = 0; i < newvocabpasted.length; i++) {
//search online for list of synoynyms of newvocabpasted[i]
q1 = "https://words.bighugelabs.com/api/1/754ccc845dff7cb4459e3b40365609fb/",
q2 = "/",
q3 = "json";
query = q1+newvocabpasted[i]+q2+q3;
$.getJSON(query, function(data) {
obj = data;
olength = obj.length;
console.log(obj);
// check array lengths work - which they do!
console.log("Search Length="+olength);// this displays in console as a number
console.log("Text Length="+newtextpasted.length);
console.log("Vocab Length="+newvocabpasted.length);
});
for (j = 0; j < newtextpasted.length; j++) {
console.log("J loop works");
// the loop below doesn't seem to run
for (m = 0; m < olength; m++) {
console.log("M loop works");// I don't see this
console.log(obj[m]+"/"+newtextpasted[j]);// this doesn't run
if (obj[m] === newtextpasted[j]) {
console.log("Match!");// I don't see this
} else {
console.log("Checked!");// or this!
}
}
}
}
//document.getElementById('result').innerHTML=newtextpasted;
}
The output in the console shows the result of everything correctly, apart from the 'm' loop.
Thanks to comments made - I learned about the asynchronous behaviour of the code.
I changed the getJSON part to the following...
$.ajax({
url: query,
dataType: 'json',
async: false,
success: function(data) {
obj = data;
olength = obj.length;
console.log(obj);
// check array lengths work - which they do!
console.log("Search Length="+olength);
console.log("Text Length="+newtextpasted.length);
console.log("Vocab Length="+newvocabpasted.length);
}
});
This made it synchronous (and therefore worked)... but is to be avoided (as is deprecated)... so need to learn more!

Variables not what I expect in a loop

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.

How can I execute a statement AFTER a loop finishes in javascript?

I'm querying a mongo database to retrieve the tiles for the display in rougelike game. This is the function I use:
function get_display(){
var collections = ['austinsroom'];
var db = mongojs(uri, collections);
var temphtml = '';
for(var j = 0; j < 3; j++) {
console.log("y=" + String(j));
db.austinsroom.find({"y": j}, {}).sort({"x": 1}, function(err, records) {
if(err) {
console.log("There was an error executing the database query.");
return;
}
var i = records.length;
while(i--) {
temphtml += records[i].display;
}
temphtml += '<br>';
//console.log(temphtml);
//return temphtml;
//THE ONLY WAY I CAN GET ANYTHING TO PRINT IN THE CONSOLE IS IF I PUT IT INSIDE THE LOOP HERE
});
//console.log(temphtml);
//return temphtml;
//THIS DOES NOTHING
}
//console.log(temphtml);
//return temphtml;
//THIS DOES NOTHING
}
get_display();
If I put the console.log(temphtml) inside the loop, it prints out three times which isn't what I want. I only want the final string (i.e. ...<br>...<br>...<br>. Also I can't ultimately return the temphtml string, which is actually the important thing. Is this some quirk of javascript? Why would it not execute statements after the loop?
Also: is there a better way to retrieve every element of a grid that's stored in a mongo database, in order, so it can be displayed properly? Here's what the mongo documents look like:
{
"_id": {"$oid": "570a8ab0e4b050965a586957"},
"x": 0,
"y": 0,
"display": "."
}
Right now, the game is supposed to display a "." in all empty spaces using the x and y values for the coordinates. The database is indexed by "x" values.
See async.whilst. You want flow control of the for loop, for which this provides a callback to control each loop iteration.
var temphtml = "",
j = 0;
async.whilst(
function() { return j < 3 },
function(callback) {
db.austinsroom.find({"y": j }, {}).sort({"x": 1}, function(err, records)
temphtml += records.map(function(el) {
return el.display;
}).join("") + '<br>';
j++;
callback(err);
});
},
function(err) {
if (err) throw err;
console.log(temphtml);
}
)
Either that or use Promise.all() on collected promises to return "one big result". But you would also need to switch to promised-mongo from mongojs, as the nearest equivalent, since there are more mongodb drivers that actually support promises. That one is just the direct fork from mongojs:
var temphtml = "",
j = 0,
promises = [];
for ( var j=0; j < 3; j++ ) {
promises.push(db.austinsroom.find({"y": j }, {}).sort({"x": 1}).toArray());
promises.push('<br>'); // this will just join in the output
)
Promise.all(promises).then(function(records) {
temphtml += records.map(function(el) {
return el.display;
}).join("");
})
Not exactly the same thing, since it's one list output and not three, but the point is that the Promise objects defer until actually called to resolve, so you can feed the paramters in the loop, but execute later.
I do not use MongoDB but from what I am reading it is asynchronous. So what is happening is your db.austinsroom.find call fires another "thread" and returns to the for loop to continue the next iteration.
One way to do what you want is have a check at the end of your db.austinsroom.find function to see if you're done with the for loop. Something like:
function get_display()
{
var collections = ['austinsroom'];
var db = mongojs(uri, collections);
var temphtml = '';
var doneCounter = 0;
for(var j = 0; j < 3; j++)
{
console.log("y = " + String(j));
db.austinsroom.find({"y": j}, {}).sort({"x": 1}, function(err, records)
{
if(err)
{
console.log("There was an error executing the database query.");
return;
}
var i = records.length;
while(i--)
{
temphtml += records[i].display;
}
temphtml += '<br>';
// we're done with this find so add to the done counter
doneCounter++;
// at the end, check if the for loop is done
if(doneCounter == 3)
{
// done with all three DB calls
console.log(temphtml);
}
});
}
}
This is probably not the best way as I know nothing about MongoDB but it should put you on the right path.

Where to put the callback?

db.listCollections().toArray(function(err, collections){
for(i = 1; i < collections.length; i++){
if(i < parseInt(collections.length)){
var collect = db.collection(collections[i].name.toString(),function(){
collect.count(function(err,result){
console.log(collections[i].name.toString());
corrCount = corrCount + result;
});
});
}else{
collect = collections[i].name;
}
}
});
So my problem is that collect ends up being undefined and thus one can't count the amount of entries in the collection. Something tells me that I should solve this with callback but I keep failing. Also I don't understand why the console prints out 2 times before it shots of the error. I'm using nodejs with mongodb native driver.
You need to move the rest of the code out of the callback, like so:
db.listCollections().toArray(function(err, collections) {
for (i = 1; i < collections.length; i++) {
if (i < parseInt(collections.length)) {
var collect = db.collection(collections[i].name.toString());
collect.count(function(err, result) {
console.log(collections[i].name.toString());
corrCount = corrCount + result;
});
} else {
collect = collections[i].name;
}
}
});

Custom function for Google Spreadsheet always return the same result

I am trying to build a simple script to work with a Google Spreadsheet that looks like this:
It shows me which of my clients use which modules of each layer. Each client may use how many modules he wants to.
What I need to do is count how many clients have all the modules installed (or the three layers, it's the same).
I've tried to do this using the built-in functions but have not succeed.
Now I'm trying to do my own function, that looks like this:
function countTotalModByClient(values) {
var quantMod=0;
var quantClient=0;
for (var i=0; i<values.length; i++) {
for(var j=0; j<values.length; j++) {
if(values[i][j]=="X") {
quantMod++;
}
}
if(quantMod==15) { // total number of modules
quantClient++;
}
quantMod=0;
}
return quantClient;
}
But it always return the same result: 0.
At my sheet, I'm calling the function like this: =countTotalModByClient(B3:P6)
P.S.: Sorry about the magic number in the code, I´ll fix this. =)
This should be possible with a standard formula (although maybe a little complex).
=countif(ArrayFormula(MMULT(--(B3:P6="X"), transpose(column(B3:P2)^0))), 15)
should return the number of clients with a count of 15 in their row..
Can you check if that works ?
Or if you prefer a custom function, give this a try:
function countTotalModByClient(values) {
var quantClient = 0;
for (var i = 0; i < values.length; i++) {
if (values[i].countItem("X") === 15) quantClient += 1;
}
return quantClient;
}
Array.prototype.countItem = function (item) {
var counts = {};
for (var i = 0; i < this.length; i++) {
var num = this[i];
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
return counts[item] || 0;
}
Example spreadsheet

Categories

Resources