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

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.

Related

Global variable updated in the callback function referenced by the csv-parse module (npm) is not updating

As part of a project with a client, I am developing a workflow on their engine (built partially on top of node.js) and I have a problem with the Javascript task. In particular, I am taking a csv-file accountCsv as an input, then I want to modify a process variable outputFinanceAccounts which should be a string containing some information read from the csv-file. Further, I want to pass this process variable to the next activity of the workflow.
Inside the Javascript task, I am using the module csv.parse (npm package that is available on the client's engine), and in general it works well and reads the data from the csv file correctly. This function uses an async callback function as an argument. Further, I am changing the process variable outputFinanceAccounts inside this async function. During testing I see that this process variable is assigned with correct value inside the parsing block. However, when the csv.parse function is finished working, this updated value of the process variable outputFinanceAccounts is lost. Therefore, I can’t pass any value of this process variable to the next activity of the workflow.
I tried to create a nested function inside csv.parse and return the result of this function outside of csv.parse, but it does not work. From what I understand, this trick would work only if csv.parse would be a function, which it is not really, and I don't know what would be the right syntax for that.
const csv = require('csv');
const files = require('files')
const DepartmentColumn = 'Department'
const options = {auto_parse: true, columns: true, relax_column_count: true }
// Read the reportCsv file (workflow variable)
const csvContents = files.getContent(accountCsv.id)
outputFinanceAccounts = 'VALUE BEFORE PARSING'
console.log('Before parsing', outputFinanceAccounts);
csv.parse(csvContents.buffer.toString('utf-8'), options, (error, AccountList) => {
if (error) {
console.log(`Error parsing CSV: ${error}`)
return
}
if (AccountList.length == 0) {
console.log(`Error: CSV contains zero rows`)
return
} else {
console.log(`${AccountList.length} rows read from CSV`)
}
AccountList.forEach(account => {
if(account[DepartmentColumn] == 'Finance'){
outputFinanceAccounts = "VALUE INSIDE PARSING";
console.log('Inside parsing', outputFinanceAccounts);
}
});
})
console.log('After parsing:', outputFinanceAccounts);
Testing the code on the client's workflow engine (built partially using node.js) gives the following results:
Test execution finished successfully
Logs
-> Before parsing VALUE BEFORE PARSING
-> After parsing: VALUE BEFORE PARSING
-> 4 rows read from CSV
-> Inside parsing VALUE INSIDE PARSING
-> Inside parsing VALUE INSIDE PARSING
But what I need to have is:
-> After parsing: VALUE INSIDE PARSING
Thanks in advance for helping me with this.
Your expected output is not possible. console.log('After parsing:', outputFinanceAccounts) always runs before csv.parse(... by the time the variable is updated, console ouput is done.
Just an illustration, but csv.parse is akin to setTimeout here:
let something = "initial"
console.log(something)
setTimeout(() => {
console.log('this always runs after final console.log')
something = "final"
}, 0)
// output is still "initial"
console.log(something)
If csv.parse is asynchronous, it's always executed in the future, meaning functions in the stack gets executed first before processing further messages
Here is MDN docs explaining Concurrency and Event Loop Model of JS.

Check if data came back from firebase using angular2

I have the following code to load data from my firebase which works but it is painfully ugly.
loadData() {
// app.component
this.loadJobCodeSubscription = this.databaseService.getData()
.subscribe(result => {
this.data = null;
var data_raw: Array<any>;
if (Object.keys(result)[0] != '$value') {
data_raw = Object.keys(result).map(function (key) {
return [key, result[key]['firstName'], result[key]['lastName']];
})
// remove extra data downloaded from firebase
jobDataRaw.pop();
jobDataRaw.pop();
}
this.jobCodeData = jobDataRaw;
}, error => {
console.log('Error downloading job codes', error)
})
}
// DatabaseService
getData() {
return this.af.database.object('/jobCodes/' + this.currentUser.company)
}
What happens is that if the branch I am querying does not have data, my "result" will display
Andi if I have data coming back, I will get something like
The only difference (that I can think of to check for) between these two data is that one has a $value key and one does not
That is why I am using that weird if statement to check if that key exists.
Is there a way to check for data in a neater way?
Also, is there a way to cast 'result' to the right format as oppose to 'any' which it currently is
Note. I am using Angular 2 with AngularFire2
First, presuming you have control of the backend, you could alter the return to be more easily understandable. Returning an empty payload is a bit confusing if you really mean "nothing".
Second, dangerous to do this line:
Object.keys(result)[0]
As Object.keys(result) may not be the same in the future, even though it may be deterministic.
I think the safest way to do this would be:
if (Object.keys(result).every(k => k != '$value')) {
That being said, I don't think there's an easier way to determine that given the information you've presented. If you continue with this approach, it would be good to make sure you are guarding your statements carefully, as it seems like
bad data could slip through in the future.
Turns out that if I change the return type of the observable to be the original firebase snapshot, the data looks much better. It allows me to call if(snapshot.val()){}
return this.af.database.object('/jobCodes/' + this.currentUser.company, {preserveSnapshot: true})

passing variables through callback functions in node.js

I posted this question yesterday but I guess I just confused everyone. I got responses like "what exactly is your question?" So I am expanding and reposting today.
The following node.js snippet is from the file "accounts.js" which is in an ETrade api library that exists in the path /lib. It should return json containing data about the accounts of the authenticated user. The authentication part is working great. I'm confused about what exactly is being done in the last line of this function:
this._run(actionDescriptor,{},successCallback,errorCallback);
Ten years ago (the last time I was coding), we didn't have the construct "this" and I haven't a clue about "_run" and Google searches have not been helpful. Here is the function.
exports.listAccounts = function(successCallback, errorCallback) {
var actionDescriptor = {
method: "GET",
module: "accounts",
action: "accountlist",
useJSON: true,
};
this._run(actionDescriptor, {}, successCallback, errorCallback);
};
I understand that the function is accessed with "et.listAccounts ...." but then my understanding goes all to hell. It's pretty obvious that a get is being executed and json data returned. It's also obvious that the result is passed back through the successCallback.
In my app.js file, I have the following:
var etrade = require('./lib/etrade');
var et = new etrade(configuration);
Can someone please suggest a snippet to be used in app.js that will output the accounts data to the console?
It seems like the json data must be passed back through the successCallback but I'm lost on how to access it on the app.js side.
Suppose in app.js I want to put the accounts data in a variable called myAccounts. The exports.listAccounts function does not specify a return value, so I doubt I can do var myAccounts = et.listAccounts(). Likewise, myAccounts will be undefined if I try to do this: et.listAccounts(){myAccounts, error}. Finally, the listAccounts function contains two possible variable names I could use, "accounts" and "accountlist" but these turn out to be undefined at app.js.
When I put a function in successCallback in app.js to write a generic message to the console, the message appears in the log so I know I am making it into the listAccounts function and back successfully. In this case, the log also shows
"Request: [GET]: https://etwssandbox.etrade.com/accounts/sandbox/rest/accountlist.json"
From this I deduce that the data is actually being returned and is available at that end point.
Ten years ago (the last time I was coding), we didn't have the construct "this" and I haven't a clue about "_run"
this refers to the current object, further reading here. _run is just what they chose to call the function.
I have no experience with this module, but with a cursory glance at the git repo I suspect you will want to expand your app.js like so:
et.listAccounts(function(response) {
console.log(response);
});
In javascript functions are first order and so can be passed around like variables see here. listAccounts wants a function passed to it, and when it is complete it will call it with one parameters, as can be seen in etrade.js.
There is also the function errorCallback which is much the same but is called on an error. You could expand the above snippet like so:
et.listAccounts(function(response) {
console.log(response);
}, function(error) {
console.log(error);
});

Define server function for mongo based on pure javascript function

Suppose I have a pure javascript file that has several function definitions. It does not contain anything mongo-specific - these functions parse and evaluate a query on a document. I would like to use these functions for server queries (e.g. db.collection.find(...) or map-reduce). How may I do this so that the server functions can essentially invoke what's in the pure javascript file?
Example: In foobar.js -
function foo(query, doc) {
// some predicate, for now lets simulate by evaluating a boolean field called 'available'
return doc.available;
}
According to https://docs.mongodb.org/manual/tutorial/store-javascript-function-on-server/
I would have to do this:
db.system.js.save(
{
_id : "bar" ,
value : function (query, doc){
return doc.available;
}
}
);
Then invoke db.loadServerScripts();
Running the following gives me the desired results:
db.myCollection.find(function() { return bar("some query", this) ;})
However I'd like to avoid inlining the functions in this db.system.js.save call. These functions belong to a library that I do not maintain. If there are any changes to the javascript library, I would have to manually convert them to db.system.js.save calls.
I would like to run something like this in mongo shell:
load("foobar.js")
db.system.js.save(
{
_id : "baz" ,
value : function (query, doc){
return foo(query, doc);
}
}
);
Then:
db.myCollection.find(function() { return baz("some query", this); })
But I get a "ReferenceError: foo is not defined" in my queries that invoke baz. Function "foo" is clearly not in scope. Suggestions or alternatives?
Disclaimer: I understand that using native operations are preferred, in addition to moving complexity out of the javascript into my application. However I am not at the point where I have the resources to essentially fork the logic from javascript to application logic that decomposes to native ops

why array.push doesnt work?

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.

Categories

Resources