Nodejs get result from callback - javascript

i'm tryna get my array of results out of the callback method, the array is containing all of those things i want in but i can use it outside of the functions always keep undefined result. I was looking on many website for helping me to improve it but no results.
var DOMParser = require('xmldom').DOMParser;
var fs = require('fs');
var Initializer = require('./Initializer');
var StaticRoomList = Initializer.getStaticRoomList(fs, DOMParser);
console.log(StaticRoomList);
openRoomListXML = function(fs, DOMParser, callback){
try{
fs.readFile('./XML/RoomList.xml', function(e, data){
if(e){
callback(e);
}
else{
var BuffertoXMLString = String(data);
var XMLOutput = new DOMParser().parseFromString(BuffertoXMLString, "text/xml");
var XMLDocument = XMLOutput.documentElement;
callback(null, XMLDocument);
}
});
}
catch(fsException){
console.log(fsException);
}
};
getStaticRoomList = function(fs, DOMParser){
openRoomListXML(fs, DOMParser, function readList(e, XMLDocument){
if(e){
console.log(e);
}
else{
var nodeList = XMLDocument.getElementsByTagName("room");
var arrayRoomList = [];
for(i = 0; i < nodeList.length; i++){
arrayRoomList.push(nodeList[i].childNodes[0].nodeValue);
}
return arrayRoomList;
}
});
};
exports.getStaticRoomList = getStaticRoomList;

getStaticRoomList doesn't return anything since the code inside the callback function is executed long after the function is terminated. You should define another callback function for getStaticRoomListtoo.
So in the end, to obtain the array you will do something like
var StaticRoomList;
Initializer.getStaticRoomList(fs, DOMParser,function(result){
StaticRoomList = result;
});
And in the callback inside getStaticRoomList, instead of trying to return the result, you simply call the callback function of getStaticRoomList

Functions in JS are async.
You have to pass a callback function as a parameter.
Below there is an example:
getStaticRoomList = function(fs, DOMParser, callBack){
openRoomListXML(fs, DOMParser, function readList(e, XMLDocument){
if(e){
console.log(e);
}
else{
var nodeList = XMLDocument.getElementsByTagName("room");
var arrayRoomList = [];
for(i = 0; i < nodeList.length; i++){
arrayRoomList.push(nodeList[i].childNodes[0].nodeValue);
}
callBack(arrayRoomList);
}
});
getStaticRoomList(fs, DOMParser, function(error, result){
if(error != null){
//Deal with resul
}
})

Related

How to make async request in cycle?

var userName = 'realgrumpycat';
var moreAvailable = true;
var lastId = '';
while (moreAvailable)
{
getPhotosDataFromRequest(userName, lastId).then(function (data)
{
moreAvailable = data.more_available;
lastId = data[data.length - 1].id;
console.log(data);
});
}
getPhotosDataFromRequest() returns new Promise() and JSON with data. I'd like to execute this method several times at cyscle. But as I see at debugger, while loop executes so fast, that doesn't step into promise then block
Try using function recursion:
var userName = 'realgrumpycat';
var lastId = '';
var getPhotos = function()
{
getPhotosDataFromRequest(userName, lastId).then(function (data)
{
lastId = data[data.length - 1].id;
console.log(data);
if (data.more_available)
{
getPhotos();
}
});
};
getPhotos();
just as iterative alternative (as concept), but not really a solution in real life, because of performance and limits:
//i try to use here es5 only
var userName = 'realgrumpycat';
var moreAvailable = true;//<-- can be removed
var lastId = ''; //<-- can be removed
var maxRequests = 1000; //e.g. max 1000 requests
//create empty promise to resolve later
var resolveStart = null;
var request = new Promise(function(resolve){
resolveStart = resolve;
});
//append 1000 potential requests
for(var i = 0; i < maxRequests; i++) {
request = request.then(createRequestPromise);
}
//here you probably should differ the "done" rejection and other errors
request.catch(function(){});
//now resolve the first promise, with empty string, to start the request chain
resolveStart('');
function createRequestPromise(lastId) {
return getPhotosDataFromRequest(userName, lastId).then(function (data)
{
lastId = data[data.length - 1].id;
console.log(data);
//stop the chain by rejection
if (!data.more_available) return Promise.reject('done');
return lastId;
});
}

Node.js asynchronous coding difficulty

I'm trying to get multiple documents from MongoDB and send all the data in an array, but I'm having serious trouble understanding how this can be done with the event-driven Node.js.
The problem is that at the time dataArray.push(tempObject) is being executed, the tempObject["data"] = tempDataArray still has not been performed.
My code looks like this:
app.post('/api/charts', function(req, res) {
var names = req.body.names;
var categories = req.body.categories;
var dataArray = [];
for (i = 0; i < names.length; i++) {
var tempObject = {};
tempObject["name"] = names[i];
Company.find({ name : names[i] }, function(err, result) {
if (err) {
throw err;
}
var tempDataArray = [];
for (k = 0; k < categories.length; k++) {
var tempDataObject = {};
tempDataObject["name"] = categories[k];
tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"];
tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"];
tempDataArray.push(tempDataObject);
}
tempObject["data"] = tempDataArray;
});
dataArray.push(tempObject);
}
res.send(dataArray);
});
Any suggestions on how to properly achieve the desired result would be appreciated.
Use this library
https://github.com/caolan/async
And Using this code, your code will look like this:
var async = require("async");
app.post('/api/charts', function(req, res) {
var names = req.body.names;
var categories = req.body.categories;
var dataArray = [];
async.forEach(names, function(name, callback){
var tempObject = {};
tempObject["name"] = name;
Company.find({ name : name }, function(err, result) {
if (err) {
callback(err);
} else {
var tempDataArray = [];
for (k = 0; k < categories.length; k++) {
var tempDataObject = {};
tempDataObject["name"] = categories[k];
tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"];
tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"];
tempDataArray.push(tempDataObject);
}
tempObject["data"] = tempDataArray;
dataArray.push(tempObject);
callback();
}
});
}, function(err){
if(err){
res.send(err);
} else {
res.send(dataArray);
}
});
});
The Company.find() method takes a callback function as it's second parameter. This callback is to be called after the company data is retrieved from the database. This means it could be anywhere between a few milliseconds and a few hundered milliseconds until it is called after calling the Company.find() method. But the code directly after Company.find() will not be delayed; it will be called straight away. So the callback delay is why dataArray.push(tempObject) is always called before tempObject["data"] = tempDataArray.
On top of this the outer for loop will run synchronously and on each iteration a separate DB call will be made. This isn't ideal so we want to get this for loop into the callback. So we can do something like:
app.post('/api/charts', function(req, res) {
var names = req.body.names;
var categories = req.body.categories;
// we just do one DB query where all the data we need is returned
Company.find({ name : names }, function(err, result) {
if (err) {
throw err;
}
var dataArray = [];
// we iteratre through each result in the callback, not outside it since
// that would cause blocking due to synchronous operation
for (i = 0; i < result.length; i++) {
var tempObject = {};
tempObject["name"] = result[i].name;
var tempDataArray = [];
for (k = 0; k < categories.length; k++) {
var tempDataObject = {};
tempDataObject["name"] = categories[k];
tempDataObject["numbers"] = result[i]["data"][categories[k]]["numbers"];
tempDataObject["dates"] = result[i]["data"][categories[k]]["dates"];
tempDataArray.push(tempDataObject);
}
tempObject["data"] = tempDataArray;
dataArray.push(tempObject);
}
res.send(dataArray);
});
});
There are many approaches to abstract Nodes event driven nature such as Promises (which can be accessed either in ECMA Script 6 or a Promise library such as Bluebird, Async, etc.). But the above is a basic callback approach that is typically used in the likes of Express applications.
Simply change this :
tempObject["data"] = tempDataArray;
});
dataArray.push(tempObject);
To:
tempObject["data"] = tempDataArray;
dataArray.push(tempObject);
});

Node.js promises using Q

I have a Node app that I'm writing where I need to use promises for async calls.
I currently have a foreach loop being called from within a .then(function()) of a promise, but when I return the end result of the foreach, I get nothing.
In the foreach I can console.log the value of data and retrieve it, but not outside the for loop before the return?
var Feeds = function(){
this.reddit = new Reddit();
}
Feeds.prototype.parseRedditData = function(){
var _this = this;
this.getData(this.reddit.endpoint).then(function(data){
return _this.reddit.parseData(data, q);
});
}
Feeds.prototype.getData = function(endpoint){
var deferred = q.defer();
https.get(endpoint, function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
deferred.resolve(JSON.parse(body));
});
}).on('error', function(e) {
deferred.reject(e);
});
return deferred.promise;
}
var Reddit = function(){
this.endpoint = "https://www.reddit.com/r/programming/hot.json?limit=10";
}
Reddit.prototype.parseData = function(json, q){
var dataLength = json.data.children.length,
data = [];
for(var i = 0; i <= dataLength; i++){
var post = {};
post.url = json.data.children[i].data.url;
post.title = json.data.children[i].data.title;
post.score = json.data.children[i].data.score;
data.push(post);
}
return data;
}
Feeds.prototype.parseRedditData = function(){
var _this = this;
this.getData(this.reddit.endpoint).then(function(data){
return _this.reddit.parseData(data, q);
});
}
When i see this I see a "return" in the callback of the promise... I don't know why you're doing this, but I just want to be sure:
I you want this "return" to be the returned value of the function 'parseRedditData', this won't work.
The only way to return your data here is by using a callback, or a promise, like this:
Feeds.prototype.parseRedditData = function(callack){
var _this = this;
this.getData(this.reddit.endpoint).then(function(data){
callback(_this.reddit.parseData(data, q));
});
}

How to return value from helper function back to server.js NodeJS

For my project I have a server.js that calls a helper function place-search.js as shown below.
var express = require('express');
var server = express.Router();
var placeSearch = require("./helpers/place-search");
var obj = "hello";
server.use(function(req, res, next) {
console.log(req.method, req.url);
next();
});
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
/* send the confirmation back to the client */
res.status(200).send("body");
placeSearch.placeSearch(obj);
});
module.exports.server = server;
Here is my place-search.js :
var config = require("./config.js");
var Promise = require('bluebird');
var DistanceMatrix = require("./distance-matrix.js");
var GooglePlaces = Promise.promisifyAll(require("googleplaces"));
var googlePlaces = new GooglePlaces(config.apiKey, config.outputFormat);
var extract = require('./extract.js');
var combination = require('./combination_ver2.js');
var permutation = require('./permutation.js');
function placeSearch(obj) {
console.log("Inside place search!");
/**
* Place search - https://developers.google.com/places/documentation/#PlaceSearchRequests
*/
var arr = [];
var count = 0;
var rad = obj["radius"];
console.log("radius: " + rad);
var loc = obj["location"];
console.log("Location: " + loc);
var mode = obj["mode"];
var params = obj["params"];
/* client's keywords */
var arr;
var ar = [];
for (var i = 0; i < params; i++) {
arr[i] = obj[i];
console.log(arr[i]);
var param = {
location: loc,
radius: rad,
mode: mode,
keyword: arr[i]
};
ar.push(param);
}
console.log("before promises");
var promises = ar.map(function(name) {
return googlePlaces.placeSearch(name, function(response) {
arr.push(response);
console.log(response);
console.log(count++);
//If all responses have been returned
//Find combos and pass to distance-matrix
if (count == ar.length) {
var Matrix = new Array();
var result = new Array();
//to extract only lat and lng from arr.results
//Matrix = extract.extract(arr);
result = combination.combination(arr);
// NOW RESULT IS THE ARRAY OF ALL COMBINATION
// NOW RESULT IS THE ARRAY OF COMBINATIONS OF latlng pairs AND PASS IT TO FRONTEND
/*result.forEach(function(combo, index) {
console.log("combo" + combo)
DistanceMatrix.distanceMatrix(mode, combo, result.length);
});*/
// IF YOU WANT TO SEE PERMUTATION
//permutation.permutation(result);
console.log("combination results: " + result);
}
})
});
}
module.exports.placeSearch = placeSearch;
My problem is I do not know how to pass the result variable back to the server.js so that I can use that result as an input for another helper function. I can not for the life of me figure out how to do this. Any help at all would be greatly appreciated.
Well, I don't see your placeSearch function returning anything at all right now, nor doing any kind of callback. Your placeSearch function should expose a callback parameter, which then gets called once you have the answer you want to send back.
Your server file will then take action on that callback. Abbreviating your code, it'd look something like this:
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
placeSearch.placeSearch(obj, function(error, data){
/* send the data back to the client */
res.status(200).send(data);
});
});
To support that, your placeSearch function will have to call its callback when appropriate:
function placeSearch(obj, callback){
/* all the stuff you do to assemble your data */
// if (there_is_some_error):
if(err) return cb(err);
// when you have all your data available, no error has occurred
return cb(null, data);
}
Something else you might notice is that your ar.map won't work as you seem to expect. ar.map is a synchronous function, you're calling async code inside... not gonna work the way you think. It's a bit long for this post, but you should look at the async library from npm to manage an array of asynchronous requests to collect one combined result.
use callback your code looks like this:
function placeSearch(obj,callback) {
console.log("Inside place search!");
/**
* Place search - https://developers.google.com/places/documentation/#PlaceSearchRequests
*/
var arr = [];
var count = 0;
var rad = obj["radius"];
console.log("radius: " + rad);
var loc = obj["location"];
console.log("Location: " + loc);
var mode = obj["mode"];
var params = obj["params"];
/* client's keywords */
var arr;
var ar = [];
for (var i = 0; i < params; i++) {
arr[i] = obj[i];
console.log(arr[i]);
var param = {
location: loc,
radius: rad,
mode: mode,
keyword: arr[i]
};
ar.push(param);
}
console.log("before promises");
var promises = ar.map(function(name) {
return googlePlaces.placeSearch(name, function(response) {
arr.push(response);
console.log(response);
console.log(count++);
//If all responses have been returned
//Find combos and pass to distance-matrix
if (count == ar.length) {
var Matrix = new Array();
var result = new Array();
//to extract only lat and lng from arr.results
//Matrix = extract.extract(arr);
result = combination.combination(arr);
// NOW RESULT IS THE ARRAY OF ALL COMBINATION
// NOW RESULT IS THE ARRAY OF COMBINATIONS OF latlng pairs AND PASS IT TO FRONTEND
/*result.forEach(function(combo, index) {
console.log("combo" + combo)
DistanceMatrix.distanceMatrix(mode, combo, result.length);
});*/
// IF YOU WANT TO SEE PERMUTATION
//permutation.permutation(result);
console.log("combination results: " + result);
callback(null,result);
}
})
});
}
in server.js:
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
/* send the confirmation back to the client */
res.status(200).send("body");
placeSearch.placeSearch(obj,function(err,result){
if(!err){
console.log(result);
}
})
});
It seems like you're having trouble with the async operation. You'll want to return the promise from your place-search module. You'll also need to convert the callbacks from placeSearch into a promise.
EDIT: updated since googlePlaces.placeSearch doesn't return a promise
inside placeSearch
function placeSearch(obj) {
//...
var promises = ar.map(function(name) {
var placeDefer = Q.defer();
return googlePlaces.placeSearch(name, function(response) {
placeDefer.resolve(response); // or placeDefer.reject if a failure occurs
});
return placeDefer.promise;
});
return promises;
}
and in your route:
// I'm going to just assume Q promise library here
var Q = require("q");
server.post('/', function(req, res) {
/* get the object passed by the client's post request */
obj = req.body;
//console.log("Obj: " + obj);
/* send the confirmation back to the client */
res.status(200).send("body");
Q.all(placeSearch.placeSearch(obj))
.spread(function() {
var places = Array.prototype.slice.call(arguments);
// possibly call res.status(200).send("body"); here?
// only if you're trying to use the places in your response
});
});

How to return array from JavaScript function that retrieves data from text file?

I am building a Windows 8 Store app with HTML/CSS/JavaScript. I am reading in data from a text file through a function, and then putting that data into an array. I am trying to return the array through the function, but it is not working. Any help would be greatly appreciated. I've attached my code snippet.
// Load user data
var DefineUserData = function LoadUserData() {
return Windows.Storage.ApplicationData.current.localFolder.getFileAsync(loadfile).done(function (UserFile) {
return Windows.Storage.FileIO.readTextAsync(UserFile).done(function (fileResult) {
var userdata = new Object();
var dataobject = {};
var innercount;
var outercount;
var fileResultByLines = fileResult.split("\n");
for (outercount = 0; outercount <= (fileResultByLines.length - 2) ; outercount++) {
var tempArray = fileResultByLines[outercount].split(",");
dataobject.metrictitle = tempArray[0];
dataobject.numinputs = tempArray[1];
dataobject.inputs = new Array();
for (innercount = 0; innercount <= parseInt(dataobject.numinputs) ; innercount++) {
dataobject.inputs[innercount] = tempArray[innercount + 2];
}
userdata[outercount] = dataobject;
}
return userdata;
});
},
function (errorResult) {
document.getElementById("resbutton1").innerText = errorResult;
})
}
Your DefineUserData function is returning a Promise, not a value. Additionally done functions don't return anything. Instead you'll need to use then functions instead of done functions in DefineUserData and then handle add a done function (or then) to the code that calls this function.
Also, You can make your promises easier to read, and easier to work with by chaining then functions instead of nesting them.
Currently on Win7 at the office so I can't test this, but try something similar to this pseudo-code. Note then functions instead of done. The last then returns your data. Sample snippet afterwards to illustrate calling this and handling the result.
// modified version of yours
var DefineUserData = function LoadUserData() {
return Windows.Storage.ApplicationData.current.localFolder
.getFileAsync(loadfile)
.then(function (UserFile) {
return Windows.Storage.FileIO.readTextAsync(UserFile);
}).then(function (fileResult) {
var userdata = new Object();
var dataobject = {};
var innercount;
var outercount;
var fileResultByLines = fileResult.split("\n");
for (outercount = 0; outercount <= (fileResultByLines.length - 2) ; outercount++) {
var tempArray = fileResultByLines[outercount].split(",");
dataobject.metrictitle = tempArray[0];
dataobject.numinputs = tempArray[1];
dataobject.inputs = new Array();
for (innercount = 0; innercount <= parseInt(dataobject.numinputs) ; innercount++) {
dataobject.inputs[innercount] = tempArray[innercount + 2];
}
userdata[outercount] = dataobject;
}
return userdata;
},
function (errorResult) {
document.getElementById("resbutton1").innerText = errorResult;
});
}
// some other code...
DefineUserData.done(function (userdata) {
// do something
});

Categories

Resources