How to use promise bluebird in nested for loop? - node.js

I need to use bluebird in my code and I have no idea how to use it. My code contains nested loops. When the user logs in, my code will run. It will begin to look for any files under the user, and if there are files then, it will loop through to get the name of the files, since the name is stored in a dictionary. Once it got the name, it will store the name in an array. Once all the names are stored, it will be passed along in res.render().
Here is my code:
router.post('/login', function(req, res){
var username = req.body.username;
var password = req.body.password;
Parse.User.logIn(username, password, {
success: function(user){
var Files = Parse.Object.extend("File");
var object = [];
var query = new Parse.Query(Files);
query.equalTo("user", Parse.User.current());
var temp;
query.find({
success:function(results){
for(var i=0; i< results.length; i++){
var file = results[i].toJSON();
for(var k in file){
if (k ==="javaFile"){
for(var t in file[k]){
if (t === "name"){
temp = file[k][t];
var getname = temp.split("-").pop();
object[i] = getname;
}
}
}
}
}
}
});
console.log(object);
res.render('filename', {title: 'File Name', FIles: object});
console.log(object);
},
error: function(user, error) {
console.log("Invalid username/password");
res.render('logins');
}
})
});
EDIT:The code doesn't work, because on the first and second console.log(object), I get an empty array. I am suppose to get one item in that array, because I have one file saved

JavaScript code is all parsed from top to bottom, but it doesn't necessarily execute in that order with asynchronous code. The problem is that you have the log statements inside of the success callback of your login function, but it's NOT inside of the query's success callback.
You have a few options:
Move the console.log statements inside of the inner success callback so that while they may be parsed at load time, they do not execute until both callbacks have been invoked.
Promisify functions that traditionally rely on and invoke callback functions, and hang then handlers off of the returned value to chain the promises together.
The first option is not using promises at all, but relying solely on callbacks. To flatten your code you will want to promisify the functions and then chain them.
I'm not familiar with the syntax you're using there with the success and error callbacks, nor am I familiar with Parse. Typically you would do something like:
query.find(someArgsHere, function(success, err) {
});
But then you would have to nest another callback inside of that, and another callback inside of that. To "flatten" the pyramid, we make the function return a promise instead, and then we can chain the promises. Assuming that Parse.User.logIn is a callback-style function (as is Parse.Query.find), you might do something like:
var Promise = require('bluebird');
var login = Promise.promisify(Parse.User.logIn);
var find = Promise.promisify(Parse.Query.find);
var outerOutput = [];
return login(yourArgsHere)
.then(function(user) {
return find(user.someValue);
})
.then(function(results) {
var innerOutput = [];
// do something with innerOutput or outerOutput and render it
});
This should look familiar to synchronous code that you might be used to, except instead of saving the returned value into a variable and then passing that variable to your next function call, you use "then" handlers to chain the promises together. You could either create the entire output variable inside of the second then handler, or you can declare the variable output prior to even starting this promise chain, and then it will be in scope for all of those functions. I have shown you both options above, but obviously you don't need to define both of those variables and assign them values. Just pick the option that suits your needs.
You can also use Bluebird's promisifyAll() function to wrap an entire library with equivalent promise-returning functions. They will all have the same name of the functions in the library suffixed with Async. So assuming the Parse library contains callback-style functions named someFunctionName() and someOtherFunc() you could do this:
var Parse = Promise.promisifyAll(require("Parse"));
var promiseyFunction = function() {
return Parse.someFunctionNameAsync()
.then(function(result) {
return Parse.someOtherFuncAsync(result.someProperty);
})
.then(function(otherFuncResult) {
var something;
// do stuff to assign a value to something
return something;
});
}

I have a few pointers. ... Btw tho, are you trying to use Parse's Promises?
You can get rid of those inner nested loops and a few other changes:
Use some syntax like this to be more elegant:
/// You could use a map function like this to get the files into an array of just thier names
var fileNames = matchedFiles.map(function _getJavaFile(item) {
return item && item.javaFile && item.javaFile.name // NOT NULL
&& item.javaFile.name.split('-')[0]; // RETURN first part of name
});
// Example to filter/retrieve only valid file objs (with dashes in name)
var matchedFiles = results.filter(function _hasJavaFile(item) {
return item && item.javaFile && item.javaFile.name // NOT NULL
&& item.javaFile.name.indexOf('-') > -1; // and has a dash
});
And here is an example on using Parse's native promises (add code above to line 4/5 below, note the 'then()' function, that's effectively now your 'callback' handler):
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.select("score", "playerName");
query.find().then(function(results) {
// each of results will only have the selected fields available.
});

Related

Accessing outside variables inside the 'then' function

I am new to nodejs. Using bluebird promises to get the response of an array of HTTP API calls, and storing derived results in an ElasticSearch.
Everything is working fine, except I am unable to access the variables within the 'then' function. Below is my code:
Promise.map(bucket_paths, function(path) {
this.path = path;
return getJson.getStoreJson(things,path.path);
}, {concurrency:1}).then(function(bucketStats){
bucketStats.map(function(bucketStat) {
var bucket_stats_json = {};
bucket_stats_json.timestamp = new Date();
bucket_stats_json.name = path.name ==> NOT WORKING
});
});
How can I access the path.name variable within the 'then' ? Error says 'path' is undefined.
The best way to do this is to package the data you need from one part of the promise chain into the resolved value that is sent onto the next part of the chain. In your case with Promise.map(), you're sending an array of data onto the .then() handler so the cleanest way to pass each path down to the next stage is to make it part of each array entry that Promise.map() is resolving. It appears you can just add it to the bucketStat data structure with an extra .then() as show below. When you get the data that corresponds to a path, you then add the path into that data structure so later on when you're walking through all the results, you have the .path property for each object.
You don't show any actual result here so I don't know what you're ultimately trying to end up with, but hopefully you can get the general idea from this.
Also, I switched to Promise.mapSeries() since that's a shortcut when you want concurrency set to 1.
Promise.mapSeries(bucket_paths, function(path) {
return getJson.getStoreJson(things,path.path).then(bucketStat => {
// add the path into this item's data so we can get to it later
bucketStat.path = path;
return bucketStat;
});
}).then(function(bucketStats){
return bucketStats.map(function(bucketStat) {
var bucket_stats_json = {};
bucket_stats_json.timestamp = new Date();
bucket_stats_json.name = bucketStat.path.name;
return bucket_status_json;
});
});

Basic node.js - variable and Mongoose scope

the console.log(workingWeekdaysVar) line; is outside the findOne's scope, and the variable was declared outside it too, yet it's giving me null ...
when i put console.log(workingWeekdaysVar); inside the findOne's scope, it does give the right output, but this is useless for me because I wanna use workingWeekdaysVar elsewhere below.
The two commented out lines are the 2nd approach i attempted to do, but it gave me an undesirable output because this whole code is inside a complicated for loop.
How can I simply pass the fetched value of workingWeekdaysVar out of the scope?
var workingWeekdaysVar = [];
buyerSupplierFisModel.findOne(filter).then(function (combo) {
workingWeekdaysVar = combo.workingWeekdays;
//server.getWorkingWeekdays = function () { return combo.workingWeekdays };
});
console.log(workingWeekdaysVar);
//console.log(server.getWorkingWeekdays());
findOne() is an asynchronous function (it returns a promise object). This means that it returns inmediately and your next code line is run (in this case, console.log(workingWeekdaysVar);. But, since the function isn't done yet, workingWeekdaysVar is empty, and it will be empty until findOne() has done its job and returned the results in the provided chained callback .then(function (combo) {....
So if you want to do anything with the results, you'll have to do it in the callback. One option to this would be to use async / await:
(async () => {
try {
const { workingWeekdaysVar } = await buyerSupplierFisModel.findOne(filter)
console.log(workingWeekdaysVar)
} catch (e) {
console.log(`Error: ${e}`);
}
})()
Re-arranging your code a bit:
let doToResponse = (combo)=>{
workingWeekdaysVar = combo.workingWeekdays;
console.log(workingWeekdaysVar);
}
buyerSupplierFisModel.findOne(filter).then(function (combo) {
doToResponse(combo)
});
good for re-usability
My personal favorite:
buyerSupplierFisModel.findOne(filter).then(combo=> {
workingWeekdaysVar = combo.workingWeekdays;
console.log(workingWeekdaysVar);
});
The important thing is keep in mind, as Miguel Calderón says.. findOne - returns a promise. At that point you have another thread with different local (Lexical?) scope

In NodeJS, how do I make database queries inside of a Promise?

NodeJS 6.9.3
What I previously had went like this:
An outer function called "get_user()":
return database_queries.get_user(user_name)
.then(function(results_from_database) {
and that function then ran a database call, using Knex, and returned:
var dbquery = Multiline.stripIndent(function () {/*
SELECT
u.id as profile_id,
'user' as type_of_profile
FROM
user_profile u
WHERE name REGEXP "[[:<:]]||user_name||[[:>:]]"
*/});
dbquery = dbquery.replaceAll('||user_name||', user_name);
return DB.knex.raw(dbquery).then(function(result1) {
for(var index_of_results = 0; index_of_results < result1[0].length; index_of_results++) {
var document1 = result1[0][index_of_results];
array_of_maps_with_profile_type_and_profile_id[document1["type_of_profile"]].push(document1["profile_id"]);
}
When I did this, the database query ran, and got data, but this happened asynchronously, without the results ever being returned to the outer function. In other words, the outer function had completed long before the database queries had run.
So I tried to wrap the inner function in a Promise:
function get_user(user_name) {
return new Promise(function(resolve, reject) {
resolve ()
.then(function() {
var dbquery = Multiline.stripIndent(function () {/*
SELECT
u.id as profile_id,
'user' as type_of_profile
FROM
user_profile u
WHERE name REGEXP "[[:<:]]||user_name||[[:>:]]"
*/});
dbquery = dbquery.replaceAll('||user_name||', user_name);
return DB.knex.raw(dbquery).then(function(result1) {
for(var index_of_results = 0; index_of_results < result1[0].length; index_of_results++) {
var document1 = result1[0][index_of_results];
array_of_maps_with_profile_type_and_profile_id[document1["type_of_profile"]].push(document1["profile_id"]);
}
Now the database calls don't seem to ever be called. When they run, they appear in the logs, but now there are no database queries appearing in the logs. It would seem this inner function now returns a Promise, but the "resolve()" part of the Promise is never called.
What am I doing wrong here?
Here is a bit simpler way to write essentially the same query:
function get_user(user_name) {
const regex = `[[:<:]]${user_name}[[:>:]]`;
return DB.knex('user_profile')
.where(DB.knex.raw(`?? REGEXP ?`, ['name', regex]))
.then(res => {
// do what ever you like with returned rows here
});
}
You might want to simplify that a bit.
Take a look at these example projects.
https://github.com/joicenunes/helloapp
https://github.com/joicenunes/exercicio02
https://github.com/joicenunes/exercicio03
https://github.com/joicenunes/exercicio-04
(there are more, but you can find the rest)
Also avoid "replaceAll" and use binding variables.
Finally, since you are using node 6.x, you can use a few es6 goodies (arrow functions, multi-line strings, classes, etc), make the language work for you.

Node.js, Synchronize.js and return values

I'm using this wonderful sync module, synchronize.js - http://alexeypetrushin.github.io/synchronize/docs/index.html.
I've run into a situation where I have to get the return value of the sync'd function into the scope outside of the fiber. Here's a basic example of what I'm talking about:
var records = sync.fiber(function() {
var results = ... // some synchronized function
return results;
});
Whereas records would, in theory, contain the value of resultsfrom within the fiber scope. I've been reading up on futures (fibers/futures module) and how they might be used in this situation but I have yet to come up with anything close to working. I'd love some direction and/or a solution.
edit:
For a more thorough example of what I'm looking to accomplish:
// executes a stored procedure/function
exec: function (statement, parameters) {
init();
var request = new sql.Request(),
results;
processParams(parameters, request);
var res = sync.fiber(function(){
try {
var result = sync.await(request.execute(statement, sync.defers('recordsets', 'returnValue')));
results = result.recordsets.length > 0 ? result.recordsets[0] : [];
return results;
}
catch (e) {
console.log('error:connection:exec(): ' + e);
throw(e);
}
});
// though typical scope rules would mean that `results` has a
// value here, it's actually undefined.
// in theory, `res` would contain the return value from the `sync.fiber` callback
// which is our result set.
return res;
}
As you can see here, what I'd like to accomplish is to get the value of results in the primary scope, from the fiber's scope.
Now it does support it, use following form
var records = sync.fiber(function() {
var results = ... // some synchronized function
return results;
}, function(err, results){... /* do something with results */});
It's not a scope problem. This wont work because return res; executes before the fiber returns. That is why it's undefined.
You need to rewrite your exec function to take a callback. Then you could use synchronize.js on the exec function itself.

Returning an Array using Firebase

Trying to find the best-use example of returning an array of data in Node.js with Q library (or any similar library, I'm not partial) when using Firebase .on("child_added");
I've tried using Q.all() but it never seems to wait for the promises to fill before returning. This is my current example:
function getIndex()
{
var deferred = q.defer();
deferred.resolve(new FirebaseIndex( Firebase.child('users').child(user.app_user_id).child('posts'), Firebase.child('posts') ) );
return deferred.promise;
}
function getPost( post )
{
var deferred = q.defer();
deferred.resolve(post.val());
return deferred.promise;
}
function getPosts()
{
var promises = [];
getIndex().then( function (posts) {
posts.on( 'child_added', function (_post) {
promises.push( getPost(_post) );
});
});
return q.all(promises);
}
The problem occurs in getPosts(). It pushes a promise into your array inside an async function--that won't work since q.all is called before the promise objects have been added.
Also, child_added is a real-time event notification. You can't use that as a way to grab "all of the data" because there is no such thing as "all"; the data is constantly changing in real-time environments. FirebaseIndex is also using child_added callbacks internally, so that's not going to work with this use case either.
You can grab all of the posts using the 'value' callback (but not a specific subset of records) as follows:
function getPosts() {
var def = q.defer();
Firebase.child('users').once('value', function(snap) {
var records = [];
snap.forEach(function(ss) {
records.push( ss.val() );
});
def.resolve(records);
});
return def.promise;
}
But at this point, it's time to consider things in terms of real-time environments. Most likely, there is no reason "all" data needs to be present before getting to work.
Consider just grabbing each record as they come in and appending them to whatever DOM or Array where they need to be stored, and working from an event driven model instead of a GET/POST centered approach.
With luck, you can bypass this use case entirely.

Resources