why array.push doesnt work? - javascript

i have this code
function imagenesIn(errU,errores)
{
if(errU) throw errU;
var directorios=new Array();
var origenDir='';
var destinoDir='';
if(errores=='')
{
if(campos.img instanceof Array)
{
for(file in campos.img)
{
origenDir='';
destinoDir='';
origenDir=campos.img[file].path;
destinoDir='/uploads/publish/alquiler/'+req.session.passport.user+campos.img[file].name;
fs.rename(origenDir,process.cwd()+'/public'+destinoDir,function(err)
{
if (err) throw err;
directorios.push(destinoDir);
console.dir(directorios)
})
}
}
}else{
res.send(errores)
}
return directorios;
},
i want to get in directorios an array of the destiny of all files content in req.files.img
that are in campos.img
but when i print in console this happend
"img": [
"/uploads/publish/alquiler/andres#hotmail.comTulips.jpg",
"/uploads/publish/alquiler/andres#hotmail.comTulips.jpg"
],
im trying to get this result
"img": [
"/uploads/publish/alquiler/andres#hotmail.comTulips.jpg", //first img
"/uploads/publish/alquiler/andres#hotmail.flowers.jpg"//second img
],
why .push() method put only the first image directory and not the second???
i miss something???
tnx

Your problem is that in
fs.rename(origenDir,process.cwd()+'/public'+destinoDir,function(err)
{
if (err) throw err;
directorios.push(destinoDir);
console.dir(directorios)
})
your push() won't have actually run by the time you do
return directorios;
You need to make sure that the call to fs.rename(...) that finishes last (which is not, I repeat not, necessarily going to be the same call that starts last) handles the case where all the calls have finished. Using asynchronous calls, you cannot just fall through after firing up a bunch of them and do a return; you will have to put the code that you want to run after all the work is done in a callback that addresses what I called "handles" earlier.
Control-flow libraries like async.js could simplify your code, but you'll need to get your head around the notion that once your function goes async everything that follows it has to be async as well.

ebohlman pretty much called it. Right now your for loop is setting up the anonymous functions that the rename function will call once it finishes.
As soon as these are setup, imagenesIn will return directories. It may contain some or none of the directories, depending on whether or not rename finished before your return.
The power of node is that it is asynchronous. You could use fs.renameSync yes, and it would follow what you expect. Node is not like an apache php server. The php server gets a request, and reserves a small slice of memory for the request. That's why other requests can still be processed, because they all get their own memory. Node doesn't do this. It runs on a single thread and if you do anything that is blocking (like synchronous IO), other requests have to wait until it's finished before they can be processed.
Ideally, your imagenesIn should also be asynchronous as well, taking a function as the final parameter. The standard for the function usually follows function(error, data). Error should be null if there was none. fs.rename follows this pattern.
Also the function that calls imagenesIn should ideally handle the server response. This allows the function to be used in other types of cases. What if you don't want to send that specific response on error? What if you don't want to send a response at all? Right now this is a good recipe for accidentally sending headers twice (and getting an error).
If it were me, this is how I would write your function (I didn't test but should give you some direction).
function imagenesIn(callback) {
var directorios=new Array();
var origenDir='';
var destinoDir='';
if(campos.img instanceof Array) {
function recursion(index){
//the case that ends the recursion and returns directories
if (index >= campos.img.length) {
callback(null, directorios);
return;
}
origenDir=campos.img[index].path;
destinoDir='/uploads/publish/alquiler/'+req.session.passport.user+campos.img[index].name;
fs.rename(origenDir, process.cwd() + '/public' + destinoDir, function(err) {
//the case that ends recursion and sends an error
if (err) {
callback(err);
return;
}
directorios.push(destinoDir);
console.dir(directorios);
recursion(index++);
})
}
recursion(0);
}
else {
callback('Campos.img was not an array.');
}
}
and your code that calls it might look something like this
imagenesIn(function(err, directories) {
if (err) {
res.send(err);
}
else {
//do cool stuff with directories.
}
});
Also, I wanted to make sure you understood the unique difference between for( ; ; ) and for(key in object). "For in" iterates through the keys of an object. This works on an array because it is essentially an object with numeric keys. I could do this however
var array = ['data', 'data'];
array.nonNumericKey = 'otherdata';
If you did for (var i = 0; i < array.length; i++), you would only iterate through the array data. If you used for (key in array), you would also iterate through the nonNumericKey. This is why personally, I only use "for in" on objects that are not arrays.

Related

How to change json value in a nested (mongoose) callback

I'd like to change a json array within a nested function that I created as a callback within a mongoose query.
I am puzzled why it is not possible to change a json array within this scope. Any attempt results in the original value of 'default' which can be shown by the last console.log(answer)
As I am an Embedded C developer with newbie skills in javascript (at most), I'd like to know how to alter any value in a nested scope. And please share some background why my function shows this type of behaviour.
Any help is warmly welcome...
function pwdGen(body) {
var answer = [{
error: errors["E004"],
user: '',
password: 'default',
timeout: 0,
active: false
}];
user.find().where("usertype").equals("superuser").select("username password").exec(
function (err, users) {
if(users.isArray && users.length==0) {
console.log("exists");
answer[0].password="exist_pwd";
} else {
console.log("empty");
answer[0].password="empty_pwd";
}
}
);
answer[0].user="asdave"
answer[0].timeout=1;
console.log(answer);
return answer;
}
Problem
Javascript is async in nature means it does not execute instructions such as network access synchronously like C, C++ or Java.
Solution
Promises are here for rescue.
You need to do something like this:
var users = user.find().where("usertype").equals("superuser").select("username password").exec();
users.then(function(user) {
if(users.isArray && users.length==0) {
console.log("exists");
answer[0].password="exist_pwd";
} else {
console.log("empty");
answer[0].password="empty_pwd";
}
})
EDIT
It's just that the JS is single threaded means one thing at a time but when it sees any network access request like API call, it moves that instruction to somewhere else in the browser where its gets executed and JS continue with the following instructions when all instructions are executed, the moved part which was executed somewhere else, it's result get merged.
Excellent resource to get the idea.

Inconsistent results from elementLocated vs findElements

I am writing Webdriver automation for a web app. I have a test that looks like this:
it('has five items', async function(done) {
try {
await driver.wait(until.elementLocated(By.className('item-class')),5000);
const items = await driver.findElements(By.className('item-class'));
expect(items.length).toBe(5);
done();
}
catch(err) {
console.log(err)
}
}
This test will pass about 2/3 of the time, but will sometimes fail with:
Expected 0 to be 5.
I would think that there should be no way to get this response, since the first line is supposed to make it wait until some of these items exist. I could understand a result of "Expected 1 to equal 5.", in the case that one item was added to the page, and the rest of the test completed before they were all there, but reaching the expect() call with 0 items on the page does not make sense to me.
The questions, then, are:
1) What am I missing / not understanding, such that this result is in fact possible?
2) Is there a different construct / method I should be using to make it wait until the expected items are on the page?
I checked the source code and elementLocatedBy uses findElements, see here. And findElements can return an empty array of elements after the timeout and hence 0 is expected (learnt something new today).
You can write something custom or use some ready-made method from here that doesn't use findElements
driver.wait(async function() {
const items = await driver.findElements(By.className('item-class'))
return items.length > 0;
}, 5000);
well I think a good way to solve this issue would be
try {
const items = await driver.wait(until.elementsLocated(By.className('item-class')));
return items.length > 0;
}
catch(err) {
console.log(err)
}
this way will always wait for ALL elementS (it's elementSlocated) to be located and will return an array of items (remember that without await it will return an array of promises).
It has no timeout so it will wait until they are all ready (or you can put a limit so if something weird is happening you can see it).

PouchDB gives 409 "Document update conflict" error with .put(), even with correct ._rev

I have a function that may either create or update documents in PouchDB, as follows. The function works perfectly when run the first time. However, every subsequent run yields a 409 error, even though the ._rev property appears to be correct.
function saveEventMatchesToDatabase(event, db) {
/* event: An event object from The Blue Alliance API. */
/* db: a reference ot the PouchDB database. */
/* Purpose: Given an event, extract the list of matches and teams, and save them to the database. */
TBA.event.matches(event.key, function(matches_list) {
var i = 0;
for (i = 0; i < matches_list.length; i++) {
var match = new Object();
var docrec = new Object();
match._id = 'matches/' + matches_list[i].key;
match.redTeam = matches_list[i].alliances.red.teams;
match.blueTeam = matches_list[i].alliances.blue.teams;
/* If the doc already exists, we need to add the _rev to update the existing doc. */
db.get(match._id).then(function(doc) {
match._rev = doc._rev;
docrec = doc;
}).catch(function(err) {
if ( err.status != 404 ) {
/* Ignore 404 errors: we expect them, if the doc is new. */
console.log(err);
}
});
db.put(match).then(function() {
// Success!
}).catch(function(err) {
console.log('\ndoc._rev: ' + docrec._rev);
console.log('match._rev: ' + match._rev);
console.log(err);
});
}
});
}
Sample console output from running this function the second time is below. The same error occurs for EVERY item in match_list, not just intermittently.
doc._rev: 1-7cfa2c6245dd939d8489159d8ca674d9
match._rev: 1-7cfa2c6245dd939d8489159d8ca674d9
r {status: 409, name: "conflict", message: "Document update conflict", error: true}
I'm not sure what I'm missing, that's causing this problem. Any suggestions for where to look next would be greatly appreciated.
The first problem seems to be that you're using a function within a loop, meaning that any variables used inside of the inner functions are randomly changing under your feet depending on when the function gets invoked. Instead of a for loop, you can use forEach().
However, the second problems is that you are not using promises correctly; you need to wait for the result of get() before you do your put(). So probably forEach() is not even what you want in the first place; you probably want to use Promise.all() and then compose your promises.
I wrote a piece on this awhile back; many people have told me that it's worth reading even though it's long: "We have a problem with promises." Read that, and you should hopefully understand promises by the end of it. :)

Why is console.log() not working client-side when inside Meteor methods?

I want to define the same method for both client and server, so I have the following code inside common/methods.js.
Meteor.methods({
doSomething: function (obj) {
if(this.isSimulation) {
console.log('client');
}
if(!this.isSimulation) {
console.log('server');
}
console.log('both');
return "some string";
}
});
Then I called this method inside client/dev.js.
Meteor.call('doSomething', someObj, function (e, data) {
console.log(e);
console.log(data);
});
On the server's console, I can read:
I20150622-21:56:40.460(8)? server
I20150622-21:56:40.461(8)? both
On the client's (Chrome for Ubuntu v43.0.2357.125 (64-bit)) console, the e and data arguments are printed, but the console.log() from the Meteor method is not, where I expected it to output the strings
client
both
Why do console.log() not work on the client inside Meteor methods?
To debug, I split the Meteor.methods into separate client and server code. Then introducing a large loop so the server-side operation so it takes a long time to complete, while the client-side is very quick.
server
doSomething: function (obj) {
var x = 0;
for(var i = 0; i< 9999999; i++) {
x++;
}
console.log(x);
return "some string";
}
client
doSomething: function (obj) {
console.log('client');
}
Still, no message is printed on the client.
Thanks to #kainlite for helping me debug this together. It turns out the problem was a simple one of file load order.
I defined my methods in common/methods.js, whereas my client-side calls were made in client/dev.js, which gets loaded first.
So when the call was made the method wasn't defined, hence it won't run. Moving the methods.js file inside the /lib directory fixed the issue.
Methods are only executed on the server, they are the sync way of doing a remote call.
Methods
Methods are server functions that can be called from the client. They
are useful in situations where you want to do something more
complicated than insert, update or remove, or when you need to do data
validation that is difficult to achieve with just allow and deny.
http://docs.meteor.com/#/basic/Meteor-users

Chaining methods on-demand

The title is probably really bad, so sorry for that :/
I have a library that creates users for me with predefined capabilities. Right now that works by doing something like
var User = require(...).User;
var user = new User(...);
// user has methods like which are all asymc
user.register(callback);
user.addBla(callback);
I also have wrapper methods which work like:
lib.createUser.WithBla(callback)
however, that naturally does incur a huge number of methods once you think of various combinations etc. So I have two ideas:
somehow make those calls chain-able without having to do huge levels of callback-function-juggling. eg. lib.createUser(callback).WithBla().WithBlub().WithWhatever()...
passing some sort of capabilities like lib.createUser({Bla:true, Blub:true}, callback)
however I have not the slightest clue how to actually implement that, considering all those methods are asynchronous and use callbacks (which I cannot change, as they are based on the node-module request).
Maybe not quite what you had in mind, but you could use the library async for this.
var user = new User();
user.addSomeValue = function(someValue, cb) { cb(null) }
// Execute some functions in series (one after another)
async.series([
// These two will get a callback as their first (and only) argument.
user.register,
user.addBla,
// If you need to pass variables to the function, you can use a closure:
function(cb) { user.addSomeValue(someValue, cb); }
// Or use .bind(). Be sure not to forget the first param ('this').
user.addSomeValue(user, someValue)
], function(err, results) {
if(err) throw "One of the functions failed!";
console.log(
"The the various functions gave these values to the callbacks:",
results;
);
});
The result is a single callback, not many nested ones.
Another option would be to re-write your code to use Promises.

Categories

Resources