I am trying to fetching all entries for a particular key patterns and to make the callback happen neatly, I am using Bluebird. The redis client for nodejs is node_redis for the project.
The code in redis client is -
exports.getAllRedisKeysA = function() {
var res = rclient.keysAsync("client*").then(function(data) {
// console.log(data);
}).then(function(data) {
var arrayResp = [];
for (var element in data) {
rclient.hgetallAsync(element).then(function(data) {
arrayResp.push(data);
});
};
return arrayResp;
// console.log(data);
}).catch(console.log.bind(console));
console.log(res); // gives an empty promise.
return res;
}
And this function is being called from a controller in the manner below -
var abc = rdata.getAllRedisKeysA();
// console.log(abc); // gives undefined
The console.log output inside the redis function gives an empty promise and nothing is returned to the controller.
Am I missing anything in the implementation?
Linus and Jaromanda had some real helpful comments to the question that helped me move in the right direction. I have used the below way to fetch my required data from REDIS using BlueBird Promise and here's how this needs be done.
The code below gets the required data from REDIS
exports.getRedisKeyPattern = function(pattern){
var allKeys = allKeysPattern(pattern).then(function(data){
var arrayResp = [];
var inptArr = [];
var newdata = data.slice(1)[0];
for(var i in newdata){
inptArr.push(newdata[i]);
};
return new Promise.resolve(inptArr);
});
var valuePerKey = Promise.mapSeries(allKeys, function(dt){
return getAllKeyContents(dt);
}).then(function(res){
return res;
}).catch(function(err) { console.log("Argh, broken: " + err.message);
});
return new Promise.resolve(valuePerKey);
}
function getAllKeyContents(key){
var data = rclient.hgetallAsync(key).then(function(data){
return data;
}).catch(function(err) { console.log("Argh, broken: " + err.message); });
var res = Promise.join(data, key, function(data, key){
return {
key: key,
data: data
};
});
return res;
}
From the controller, the function is called like this -
var rdata = require('../../components/redis/redis-functions');
rdata.getRedisKeyPattern("clients*").then(function(response){
return res.status(200).json(response);
});
The .js file which contains the redis functions is included into the controller file so that functions can be used.
Related
I'm trying to do a return of JSON object using a Bluebird's Promise.mapSeries/Promise.map within a Promise.mapSeries/Promise.map but I am unable to return them correctly. The function is as follows:
function getMovieDetails(link){
return new Promise(function(resolve, reject) {
request(link, function(error, response, body){
var request = Promise.promisifyAll(require("request"), {multiArgs: true});
var $ = cheerio.load(body);
var movieYears = "Years";
var movieYearLinks = [];
movieYearLinks.each(function(index, item) {
var movieYear = $(item).text();
var movieYearLink = $(item).attr("href");
movieYearLinks.push(movieYearLink);
});
Promise.mapSeries(movieYearLinks, function(url) {
return request.getAsync(url).spread(function(response,body) {
var $ = cheerio.load(body);
var movie = {};
var year = "YEAR".text();
movie["year"] = year;
var movieActorsArray = [];
movieActors.each(function(index, item){
var movieActor = $(item).text();
movieActorsArray.push(movieActor);
});
movie["movieActors"] = movieActorsArray;
var recommendedMovies = //SOME ROWS;
var recommendedMoviesLinks = [];
recommendedMovies.each(function(jndex, jtem){
var recommendedRowObject = {};
var recommendedText = .text();
var recommendedLink = .attr("href");
recommendedRowObject["recommendedText"] = recommendedText
recommendedRowObject["recommendedLink"] = recommendedLink
recommendedMoviesLinks.push(recommendedLink);
});
Promise.mapSeries(recommendedMoviesLinks, function(url) {
return request.getAsync(url).spread(function(response,body) {
var $ = cheerio.load(body);
var obj = {};
// GET SOME OTHER DESCRIPTION FROM THE RECOMMENDED MOVIE LINK
return obj;
});
}).then(function(results) {
// results here returns an array of recommended movie links objects.
}).catch(function(err) {
});
return main;
});
}).then(function(results) {
// results here returns an array of movies
// I want to be able to link each movie with its recommended movies.
}).catch(function(err) {
console.log("Big Error " + err);
});
});
});
}
Some explanation of my code and context.
The home page is a page with movie years. I've looped through them to get the links of the years and put into an array movieYearLinks.
Next, I've used Promise.mapSeries to get some basic info on the movies. Within that basic info, there is are recommended movie link listed in a table. I've looped them through and put them into an array, recommendedMoviesLinks.
After which, I'm going in to grab some other recommended movie (recommendedMovieObject) info before.
I want to create a JSON object with all these info. The JSON object should be something like
{ movieYear: 2017, movieLink: ..., recommendedMovies (This is an array
of recommendedMovieObjects): }
I'm open to any solution to achieve this. Thank you in advance.
You don't need to nest promises, the whole point of promises is not having to nest callbacks. A new promise need to either be rejected with an error or resolved with a value. Your code should be something like :
let getMovieDetails = function(link){
return new Promise(function(resolve,reject){
request(link,function(err,response,body){
err ? reject(err):resolve(body)
}
}).then(function(body){
// do some cherio stuff
return dataList
}).then(function(dataList){
return Promise.all(dataList.map(function(d){return // a promise of d})
})
}
let finalData = getMovieDetails(link).then(function(data){
//do some computation
}).catch(function(e){ // handle errors })
Depending on your version of node there is no need for bluebird at all. Javascript supports promises.
I'm having trouble figuring out why my data is not being push into my new array, "results". newArr[0].mscd.g[i] is a list of several objects.
var axios = require('axios');
var moment = require('moment');
var _ = require('lodash');
var getData = function() {
return getNBASchedule().then(function(payload) {
return filterByMonth('January', payload);
}).then(function(result) {
return result
});
}
....
getData grabs the data from baseURL and returns a list of objects.
var getMonthlySchedule = function(data){
var results = [];
var newArr = data.slice(0, data.length);
for (var i = 0; i <= newArr[0].mscd.g.length; i++) {
if (newArr[0].mscd.g[i].v.tid === 1610612744 || newArr[0].mscd.g[i].h.tid === 1610612744) {
results.push(newArr[0].mscd.g[i]); <---- //does not seem to work
// however if I were to console.log(newArr[0].mscd.g[i],
// I would see the list of objects)
}
}
return results; <-- //when i console at this point here, it is blank
};
var getSchedule = function () {
return getData().then(function(pl) {
return getMonthlySchedule(pl)
})
};
var monthlyResults = function() {
return getSchedule().then(function(r) {
console.log("result", r)
return r
});
};
monthlyResults();
You don't know when getSchedule() is done unless you use a .then() handler on it.
getSchedule().then(function(data) {
// in here results are valid
});
// here results are not yet valid
You are probably trying to look at your higher scoped results BEFORE the async operation has finished. You HAVE to use .then() so you know when the operation is done and the data is valid.
Your code should simplify as follows :
var getData = function() {
return getNBASchedule().then(function(payload) {
return filterByMonth('January', payload);
});
}
var getMonthlySchedule = function(data) {
return data[0].mscd.g.filter(function(item) {
return item.v.tid === 1610612744 || item.h.tid === 1610612744;
});
};
var monthlyResults = function() {
return getData()
.then(getMonthlySchedule)
.then(function(r) {
console.log('result', r);
return r;
});
};
monthlyResults();
This may fix the problem. If not, then :
Check the filter test. Maybe those .tid properties are String, not Number?
Check that data[0].mscd.g is the right thing to filter.
Below is the working code to access data form firebase. It uses global variable 'Data' array to send it to the final callback function. But I don't want to declare global variable. So is there anyway I can pass my data to every callbacks after it ? below is the code.
var data = {};
getData('myKey', function(){
console.log("myCompleteData: "+ data); //both Id and finalData
});
var getData= function(key,callback) {
return fireproof.child("data").child(key)
.then(CUSTOM_getData)
.then(callback)
}
function CUSTOM_getData(snapshot) {
var id= snapshot.val().id;
data.id= id;
return {
then: function(callback) {
fireproof.child("otherData").child(data.id)
.then(CUSTOM_getSomethingFromId)
.then(callback)
}
};
}
function CUSTOM_getSomethingFromId(snapshot) {
var finalData = snapshot.val().finalData;
data.finalData = finalData;
return {
then: function(callback) {
callback(data);
}
};
}
And I am new to Node.js. So please let me know if this approach is correct :)
Working code:
var getSomeData = function(key,callback) {
var data = {};
fireproof.authWithCustomToken(nconf.get('config:someToken')).then(function(){
return fireproof.child("data").child(key)
}).then(function(snapshot){
data.id= snapshot.val().id;
return fireproof.child("otherData").child(data.id)
}).then(function(snapshot){
data.finalData = snapshot.val().finalData;
callback(data);
}, function(error){
console.log('Error: '+error);
});
}
Okay, so my code is pulling data from a yelp business using their official API. My problem is that I can't seem to get the data to return out of the function. The problem isn't in ejs, it's that the data doesn't return when I tell it to! I just get undefined with some attempts, and with others (including the one I'm going to show here), I get an empty array. I'm pasting only the code that's important, let me know if you need more!
function yelp(){
var b = [];
var i = 0;
(struck the initialization of client)
client.business("(struck)", function(error, data) {
if (error != undefined){
res.send("an error occured. exiting");
process.process.reallyExit();
}
b[i++] = data.name;
b[i++] = data.display_phone;
b[i++] = data.rating;
console.log(b); //NEW!!
});
console.log(b);
return b;
}
app.get('/yelp', function(req,res){
var arr = yelp();
console.log(arr);
res.render('yelp.ejs', {title: 'Yelp!', arr: arr});
});
}
I added one more line of code, that I THINK may have narrowed down the problem to being related to my poor internet connection. I added ANOTHER console.log(b), this time inside of the business API call. the console.log(arr) is shows second, the console.log(b); just before the reutrn shows first, and LAST is the console.log(b) INSIDE the API call. It also took a good 30 seconds for that log to appear, and it appeared AFTER the page loaded. So, how do I go about making the page wait for the data? Or is this unrelated to my problem?
Without knowing their API, I do recognize the callback style. The result of the call to your client would then be in the data parameter in the callback, and thats where you want to render your view.
The following is not tested.
function yelp(cb) {
var b = [];
var i = 0;
// (struck the initialization of client)
client.business("(struck)", function(error, data) {
if (error) {
return cb(error);
}
b[i++] = data.name;
b[i++] = data.display_phone;
b[i++] = data.rating;
});
console.log(b);
cb(null, b)
}
app.get('/yelp', function(req, res) {
yelp(function(err, arr) {
if (err) {
res.send("an error occured. exiting");
process.process.reallyExit();
return;
}
console.log(arr);
res.render('yelp.ejs', {
title: 'Yelp!',
arr: arr
});
});
});
The thing to notice here, is the callback passing. This is normally how you do
async work in Node.js. It can be made a bit prettier using promises.
nodejs is async, meaning the app won't wait for the yelp() function to return. you can pass the yelp function a callback like so:
function yelp(callback){
var b = [];
var i = 0;
(struck the initialization of client)
client.business("(struck)", function(error, data) {
if (error != undefined){
res.send("an error occured. exiting");
process.process.reallyExit();
}
b[i++] = data.name;
b[i++] = data.display_phone;
b[i++] = data.rating;
});
console.log(b);
callback(b);
}
yelp(funtion(arr) {
res.render('yelp.ejs', {title: 'Yelp!', arr: arr});
})
You are expecting sync things to happen, but it's async. Your client.business method takes in a callback as it's second argument which isn't returning by the time res.render gets called.
Try this:
function yelp(callback) {
var b = [];
var i = 0;
client.business("(struck)", function(error, data) {
if (error != undefined){
res.send("an error occured. exiting");
process.process.reallyExit();
}
b[i++] = data.name;
b[i++] = data.display_phone;
b[i++] = data.rating;
// No returns in async. Just call the callback.
callback('yelp.ejs', { {title: 'Yelp!', arr: b}})
});
}
app.get('/yelp', function(req,res){
yelp(res.render);
});
I got a question regarding the solr-client module of nodejs. I'm using this module for querying against a solr-index.
The module itself works fine as long as I don't have to wait for finishing of the query and as long I need the result only as a async result.
But currently I cannot find out, how I will be able to await the finishing of a search request and use the result in a sequential way.
I have the follwing method in my manager
SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);
var docs = null;
var finished = false;
var deferred = Q.defer();
var request = solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
var records = null;
var promise = deferred.promise;
promise.then(function(result) {
records = result;
}).fail(function(error){
records = error;
});
return records;
};
The problem here is, that I try to wait for the result of the query and use it as return value of "promisedQuery".
I try since days to use this method in a sequential call, also with different additional modules like "wait.for", "q", etc. but nothing seems to work.
The callback function of the solr-client will always be executed after the manager-method has already returned. Also the promise-methods will be even called after the return from the manager-method.
Can someone help me out on that topic or have some tips, how I can await the response of the solr-client-search operation and then give it back in a sequential way?
Thanks for any help.
Udo Gerhards
over one week, it seems now that I have found a solution:
SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);
var docs = null;
var deferred = Q.defer();
var request = solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
return deferred.promise;
};
in all other managers, which are calling the above function:
...
var dbPromise = this.solrManager.promisedQuery(query);
var _self = this;
return Q.async(function*(){
var result = yield dbPromise;
return result;
});
...
After first tests, it seems that synchronized methods will wait until the promise is settled.
The only thing is, that it runs only with NodeJs version 0.11.10, which supports generator functions, with activated --harmony-flag and "q"-module.
Best regards
Udo
You are just using the promises a bit incorrectly. Instead of returning records, you need to return 'deferred.promise'. It should look something like this (note that you don't need the callback you passed into promisedQuery).
SolrManager.prototype.promisedQuery = function(query) {
var solrClient = solr.createClient(this.configuration.cores.page),
deferred = Q.defer();
solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
return deferred.promise;
};
To use it you would do something like:
SolrManager.promisedQuery(myquery)
.then(function (data) {
// data is whatever your 'resolved' in promisedQuery
}, function (err) {
// err is whatever you rejected in promisedQuery
});
based on rquinns answer I've changed the code like follows:
SolrManager.prototype.promisedQuery = function(query, callback) {
var solrClient = solr.createClient(this.configuration.cores.page);
var docs = null;
var finished = false;
var deferred = Q.defer();
var request = solrClient.search(query, function(err,obj){
if (!err) {
if (obj.response.numFound > 0) {
deferred.resolve(obj.response.docs);
} else {
deferred.resolve(null);
}
} else {
deferred.reject(err);
}
});
return deferred.promise;
};
...
DemoObject.prototype.toString = function() {
return SolrManager.promisedQuery(this.query).then(function(result){
return result['title'];
}).fail(function(error){
return error;
});
};
DemoObject.prototype.typeOf = function() {
return SolrManager.promisedQuery(this.query).then(function(result){
return result['title'];
}).fail(function(error){
return error;
});
};
I think, this is the right way to use the "promise"-object. But what happens when i do the follwing:
...
var demoObject = new DemoObject();
demoObject.query = "id:1";
console.log(''+demoObject);
...
or if I use "demoObject" by concatenating it to a string
...
var string = "Some string "+demoObject;
...
In case of the string concatenation, I'm currently not sure that the string will contain also the title field from the database. Same for console output.
Will nodejs be so intelligent that it resolves for e.g. the string concatenation "after" the results from the database will be available?
BR
Udo