In my controller called MapController I'm doing a function to do a parse of remote json files, and from an if-else structure add some values in an array called "parsewebservice", apparently everything is working fine but console.log ( parsewebservice); is not returning the values that were passed to the array "parsewebservice" in the place where it is returning it empty. But when I put it inside the forEach it returns, but everything cluttered and repeated then is not the right way.
I wanted to know why the values that were passed to the array "parsewebservice" are not going along with the variable after populada and what would be the correct way to do it?
Here is my code below:
/**
* MapController
*
* #description :: Server-side logic for managing Maps
* #help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
*/
module.exports = {
index: function(req, res, next) {
Data.find(function foundData(err, datas) {
if (err) return next(err);
var parsewebservice = [];
datas.forEach(function(data, index) {
var req = require("request");
var url = data.address + "?f=pjson";
req(url, function(err, res, retorno) {
if (err) {
console.log(err);
} else {
var camadas = JSON.parse(retorno);
if (camadas.mapName) {
camadas.layers.forEach(function(campo, i) {
if (campo.subLayerIds != null) {
} else if (campo.subLayerIds == null) {
parsewebservice.push([i, "dynamicMapLayer", campo.name, data.address]);
}
});
} else if (camadas.serviceDataType) {
parsewebservice.push([null, "imageMapLayer", camadas.name, data.address]);
} else if (camadas.type) {
parsewebservice.push([null, "featureLayer", camadas.name, data.address]);
}
}
});
});
console.log(parsewebservice);
});
},
};
My first comment has to be that you should not combine function(req, res) with var req = require('request')... you lose your access to the original req object!
So, you need to run a list of async tasks, and do something when they are all complete. That will never be entirely easy, and no matter what, you will have to get used to the idea that your code does not run from top to bottom as you've written it. Your console.log at the bottom runs before any of the callbacks (functions you pass in) you pass to your external requests.
The right way to do this is to use promises. It looks like you are using this request library, whose returned requests can only accept callbacks, not be returned as promises. You can create your own promise wrapper for them, or use an alternative library (several are recommended on the page).
I don't want to write a whole intro-to-promises right here, so what I will do is give you a less pretty, but maybe more understandable way to run some code at the completion of all your requests.
Data.find(function foundData(err, datas) {
if (err) return next(err);
var parsewebservice = [];
// here we will write some code that we will run once per returned data
var processResponse = function(resp) {
parsewebservice.push(resp);
if(parsewebservice.length >= datas.length) {
// we are done, that was the final request
console.log(parsewebservice);
return res.send({data: parsewebservice)}); // or whatever
}
};
datas.forEach(function(data, index) {
var request = require("request");
var url = data.address + "?f=pjson";
request(url, function(err, res, retorno) {
// do some processing of retorno...
// call our function to handle the result
processResponse(retorno);
});
});
console.log(parsewebservice); // still an empty array here
});
I solved the problem.
the "request" module is asynchronous so we need to wait for it to respond and then send the response to the view.
To do this we created a function called "foo" to contain the foreach and the request, we made a callback of that function and finally we made the response (res.view) within that function, so that the controller response would only be sent after the response of the "foo" function to the callback. So we were able to parse.json the data from the "data" collection using foreach and the "request" module and send the objects to the view.
Many thanks to all who have helped me, my sincere thanks.
Related
I have those two functions where i call "http" from "Count" the "http" return promise. I want to use the return value of "http" in "Count". What I receive now is Undefined !!!
What I'm missing ?
Count Function :
Parse.Cloud.define('count', function(request, response) {
var query = new Parse.Query('MyS');
query.equalTo("Notify", true);
query.notEqualTo ("MainEventCode", '5');
query.find({
success: function(results) {
Parse.Cloud.run('http', {params : results}).then(
function(httpResponse) {
console.log('httpResponse is : ' + httpResponse.length);
response.success('Done !');
}, function(error) {
console.error(error);
});
},
error: function(error) {
response.error(error);
}
});
});
http Function :
Parse.Cloud.define('http', function(request, response) {
var query = new Parse.Query(Parse.Installation);
.
.
.
}
Relying on calling your own functions through an external interface is not a very good practice.
Now that you've realized you're going to need the same code for a different purpose, you should take the time to refactor your code such that you don't need to call the 'http' handler through Parse.Cloud.run():
function doHttp(params) {
// original implementation here
}
Parse.Cloud.define('http', function(request, response) {
doHttp(request.params)
.then(response.success)
.fail(response.error);
}
Parse.Cloud.define('count', function(request, response)) {
var query = new Parse.Query('MyS');
query.equalTo("Notify", true);
query.notEqualTo ("MainEventCode", '5');
query.find()
.then(doHttp) // doHttp will receive the results from `query` as its parameter
.then(function(httpResponses) {
// httpResponses is an array-like object as per the other question:
httpResponses = Array.prototype.slice.call(httpResponses);
httpResponses.forEach(function (response) {
console.log('httpResponse is : ' + response.length);
});
}).fail(response.error);
}
I've taken a look at the other question and as far as the implementation of count goes, I believe you're missing the point that 'http' is returning arguments, which is only an Array-like object.
This should be okay if Parse.Cloud.run runs your function on another virtual machine, but this kind of weird behaviour is another symptom of not refactoring and reusing your code through an external call (an HTTP request inside their infrastructure with JSON passing! It might greatly reduce performance and count against your requests/second quota). If Parse instead does some magic to call your function directly as if it was defined on the same environment, you're going to have problems with it not being an actual Array.
You should modify that function to return a proper array if possible. Parse CloudCode has a version of the Underscore library:
// on http
var _ = require('underscore');
Parse.Promise.when(promises).then(function() {
var results = _.toArray(arguments) // equivalent to Array.prototype.slice above
response.success(results);
}
I think what you're asking is how to use an externally callable cloud function as a step in a bigger cloud procedure. Here's how to do it: (#paolobueno has it essentially correct, with only a couple mistakes in the details).
First, let's convert that 'http' cloud function to a regular JS function. All we need to do is factor out the request and response objects. (#paolobueno has a very good idea to use underscorejs, but I won't here because its another new thing to learn).
// for each object passed in objects, make an http request
// return a promise to complete all of these requests
function makeRequestsWithObjects(objects) {
// underscorejs map() function would make this an almost one-liner
var promises = [];
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
promises.push(makeRequestWithObject(object));
}
return Parse.Promise.when(promises);
};
// return a promise to do just one http request
function makeRequestWithObject(object) {
var url = 'http://185.xxxxxxx'+ object +'&languagePath=en';
return Parse.Cloud.httpRequest({ url:url });
}
It looks like you want the updated cloud function -- rather than use params from the client -- to first make a query and use the results of that query as parameters to the http calling function. Here's how to do that. (Again, using #paolobueno's EXCELLENT practice of factoring into promise-returning functions...)
// return a promise to find MyS instances
function findMyS() {
var query = new Parse.Query('MyS');
query.equalTo("Notify", true);
query.notEqualTo ("MainEventCode", '5');
return query.find();
}
Now we have everything needed to make a clear, simple public function...
Parse.Cloud.define('count', function(request, response) {
findMyS().then(function(objects) {
return makeRequestsWithObjects(objects);
}).then(function(result) {
response.success(result);
} , function(error) {
response.error(error);
});
});
So for part of a project I am working on I have created a module that uses the Facebook graph API to make calls and return JSON data. I can console.log() the data within the method out fine and it's all good.
But I can not return the values from the methods, so I tried to use the 'async' npm module. I have followed the examples on the website, but still am struggling to get everything working, spent all day on it so far and still things arn't looking any clearer.
So what I am asking is:
Firstly I need to call the method that sets the authentication_token for the fbgraph api before I can do anything else
Then I need to call each of the functions (in any order) and get the json result for each and add it to one big json result
Finally I need to get this one big json result back into my origional javascript page (even though it's currently in a module), how would I invoke it?
Here is a snippit of my original module (only works with console.log, can't return)
module.exports = {
/**
* Pass the access token to the fbgraph module
* and call other methods to populate
*/
setUp: function (access_token) {
graph.setAccessToken(access_token);
},
/**
* Fetches users basic info:
* Age, Gender, Hometown, Relationship status
*/
getBasicInfo: function(){
graph.get("/me/", function(err, res) {
var result = {
'age' : _calculateAge(res.birthday),
'gender' : res.gender,
'hometown' : res.hometown.name,
'relationship_status' : res.relationship_status
};
//console.log(result);
return result;
}, function(){ console.log("DONE");});
},
/**
* Creates a body of text as a string
* made up of all the users status updates
*/
getPosts: function(){
graph.get("/me/feed?limit="+PHOTO_LIMIT, function(err, res) {
var posts = "";
for(var i=0;i<res.data.length;i++){
var obj = res.data[i];
if(obj.message!=undefined) posts += obj.message+"\n";
}
//console.log(posts);
return {'posts':posts};
});
},
/**
* Fetches a single dimension string array of
* photos user is tagged in
*/
getPhotos: function(){
graph.get("/me/photos?limit="+PHOTO_LIMIT, function(err, res) {
var photos = [];
for(var i=0;i<res.data.length;i++){
var obj = res.data[i];
photos.push(obj.source); // .source is full size image, .picture is a thumbnail
}
return {'photos': photos};
});
}
}
The rest of this module continues in a similar way
I have tried using parralel async
module.exports = {
getFacebookInfo: function (access_token) {
results = {};
setUp(access_token);
async.parallel([
facebookDataRequests.getBasicInfo(),
facebookDataRequests.getPosts()
Any guidence on how I should structure this would be very much appreciated, thanks in advance
I'd recommend that you structure this differently. It's not necessary to make three calls to Facebook, because you can get the info in one call as well.
So, what I'd do:
Call the Facebook Graph API using field expansion
Format results
Return results
Also, I'd advise you to use a Promise library like Q
Something like this (untested!):
module.exports = {
getFacebookInfo: function (access_token) {
setUp(access_token);
function setUp(access_token) {
graph.setAccessToken(access_token);
}
function getFBInfo() {
var deferred = Q.deferred();
graph.get("/me?fields=id,name,birthday,gender,hometown,relationship_status,feed.limit(25),photos.limit(25)", function(err, res) {
if (err) deferred.reject(err);
deferred.resolve(res);
});
return deferred.promise
}
function handleResult(result) {
var deferred = Q.deferred();
result.posts = result.feed.data
delete result.feed.data;
result.photos = result.photos.data
delete result.photos.data;
deferred.resolve(result);
return deferred.promise
}
Q.fcall(getFBInfo)
.then(handleResult)
.then(function(result) {
return result;
})
.catch(function (error) {
// Handle any error from all above steps
})
.done();
}
}
In your main file:
var FB = require('./path/to/file/above');
var results = FB.getFacebookInfo('ACCESS_TOKEN_SDAIOFHIOSDHFOSO');
I'm trying to use Node.js to get a response from an API, I want to clean the API response and use the result.
So to access the first API I have the following code.
To store and use the result I believe I need to store the JSON output globally.
However, I can't work out how to do this.
Example -
var request = require('request');
request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function(err, res, json) {
if (err) {
throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
console.log(car2go);
});
This prints the desired result however I know that this is because my variable is defined within the function.
I want to access the variable outside of the function.
To test if I could do this I changed the code to -
var request = require('request');
request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function(err, res, json) {
if (err) {
throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
});
console.log(car2go);
But if I do this I get
ReferenceError: car2go is not defined
I am running Node v0.12.2 on Mac OS Yosemite (10.10.3).
Admittedly I am very new to node and I am more familiar with R, Python and PL SQL.
There is no way to get reference to it outside of the callback function because the console.log line runs before the callback function is invoked. The reason you have to pass a callback function into the request API is because the request library needs to invoke that function when it's done making the request. Meanwhile, your app moves on and does other things (such as running that console.log line) while it waits for the callback function to fire.
That said, there are a number of ways to deal with asynchronous code. My favorite way is with promises. I use a library called bluebird for handling promises.
var request = require('request');
var Promise = require('bluebird');
var requestP = Promise.promisify(request);
The call to Promise.promisify(request) returns a new function that doesn't take a callback function, but instead returns a promise.
requestP({ url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true })
.spread(function(res, json) {
var car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
})
.then(function (car2go) {
console.log(car2go);
})
.catch(function (err) {
console.error(err);
});
Note: .spread is the same as .then except if the resolved value is an array (which it will be because the callback passed to the request library accepts 2 arguments, which bluebird will translate into an array that the promise resolves to) .spread will split up the array back into multiple arguments passed into the function you give to .spread.
Promise.resolve(['hi', 'there']).then(function (result) {
console.log(result); // "['hi', 'there']"
});
Promise.resolve(['hi', 'there']).spread(function (str1, str2) {
console.log(str1); // 'hi'
console.log(str2); // 'there'
});
You're not going to be able to return that value all the way back out to the same context from which you began the asynchronous call, but you can at least write code that looks somewhat synchronous when using promises.
Without promises you'll be forced to call functions from within functions from within functions from within functions ;)
The response is asynchronous. That means the callback function gets called sometime LATER in the future so your console.log(car2go) is executing BEFORE the callback has even been called.
The only place you can reliably use the response is inside the callback or in a function called from the callback. You cannot use it the way you are trying to. Using asynchronous responses in Javascript requires programming in an asynchronous fashion which means processing results and using results IN the asynchronous callbacks only.
Here's where the console.log() should be:
var request = require('request');
request({url: 'https://www.car2go.com/api/v2.1/vehicles?loc=wien&oauth_consumer_key=car2gowebsite&format=json', json: true}, function (err, res, json) {
if (err) {
throw err;
}
car2go = json.placemarks;
for (i = 0; i < car2go.length; i++) {
delete car2go[i].address;
delete car2go[i].charging;
delete car2go[i].exterior;
delete car2go[i].interior;
delete car2go[i].smartPhoneRequired;
delete car2go[i].vin
car2go[i].vendor = 'car2go';
car2go[i].city = 'wien';
car2go[i].carmake = 'Smart';
car2go[i].carmodel = 'Fortwo';
}
// here is where the result is available
console.log(car2go);
});
In a server made with Express in Node.js, I have the following code for handling a GET request:
function articleReturner(celien) { // function querying a database (celien is a string, cetarticle is a JSON)
Article.findOne({ lien: celien}, function (err, cetarticle){
console.log(cetarticle); // it works
return cetarticle;
});
}
app.get('/selection/oui/', function(req, res) { // the URL requested ends with ?value=http://sweetrandoms.com, for example
var celien = req.param("value"); //
console.log(celien); // it works
articleReturner(celien); // calling the function defined above
res.render('selection_form.ejs', cetarticle); // doesn't work!
});
I know that data from the URL routing is correctly obtained by the server since the console correctly displays celien (a string). I also know that the function articleReturner(celien) is correctly querying the database because the console correctly displays cetarticle (a JSON).
But res.render('selection_form.ejs', cetarticle); is not working and the console displays ReferenceError: cetarticle is not defined... What am I missing? Thanks!
Function articleReturner is executed asynchronously and return cetarticle; doesn't make a lot of sense. You need to use callbacks or promises. Here is the code that uses callback to return result from articleReturner:
function articleReturner(celien, callback) {
Article.findOne({ lien: celien}, function (err, cetarticle){
console.log(cetarticle);
callback(err,cetarticle);
});
}
app.get('/selection/oui/', function(req, res) {
var celien = req.param("value"); //
console.log(celien); // it works
articleReturner(celien, function(err, cetarticle){
res.render('selection_form.ejs', cetarticle);
});
});
I'm new to node and am having trouble understanding node's async behavior. I know this is a very frequently addressed question on SO, but I simply can't understand how to get any of the solutions I've read to work in my context.
I'm writing this module which I want to return an object containing various data.
var myModule = (function () {
var file,
fileArray,
items = [],
getBlock = function (fileArray) {
//get the data from the file that I want, return object
return block;
},
parseBlock = function (block) {
//[find various items in the block, put them into an "myItems" object, then
//take the items and do a look up against a web api as below]...
for (var i = 0, l = myItems.length; i < l; i ++) {
(function (i) {
needle.post(MY_URL, qstring, function(err, resp, body){
if (!err && resp.statusCode === 200){
myItems[i].info = body;
if (i === (myItems.length -1)) {
return myItems;
}
}
});
})(i);
}
},
getSomeOtherData = function (fileArray) {
//parse some other data from the file
}
return {
setFile: function (file) {
fileArray = fs.readFileSync(file).toString().split('\n');
},
render: function () {
var results = [];
results.someOtherData = getsomeOtherData();
var d = getBlock();
results.items = parseBlock(d);
return results;
}
}
})();
When I call this module using:
myModule.setFile('myFile.txt');
var res = myModule.render();
the variable res has the values from the someOtherData property, but not the items property. I understand that my long-running http request has not completed and that node just zooms ahead and finishes executing, but that's not what I want. I looked at a bunch of SO questions on this, and looked at using Q or queue-async, but with no success.
How do I get this module to return no data until all requests have completed? Or is that even possible in node? Is there a better way to design this to achieve my goal?
The problem in your example is your calling getBlock() but you have declared your function as getBlockData(). So you will not get a result. Try changing it to both the same.
Presuming that you have them both the same, your next problem is that your processing data from a file, so I presume that your reading the contents of the file and then parsing it.
If this is the case then there are sync reads that you can use to force sync, however I wouldn't recommend this.
You really want to structure your program based on events. Your thinking in the paradigm of 'call a function, when it returns continue'. You need to be thinking more along the lines of 'call a process and add a listener, the listener then does reply handling'.
This works very well for comms. You receive a request. You need to reply based on contents of file. So you start the read process with two possible results. It calls the completed function or the error function. Both would then call the reply function to process how to handle a reply for the request.
It's important not to block as you will be blocking the thread via which all processes are handled.
Hope that helps, if not add some comments and I will try and elaborate.
Have a look at this answer to another question to see a good example of processing a file using the standard listeners. All async calls have a listener concept for what can happen. All you need to do is pass a function name (or anon if you prefer) to them when you call them.
A quick example (based on node.js stream.Readable API:
fs.createReadStream(filename, {
'flags': 'r'
}).addListener( "data", function(chunk) {
// do your processing logic
}).addListener( "end", function(chunk) {
// do your end logic
response(...);
}).addListener( "error", function(chunk) {
// do your error logic
response(...);
}).addListener( "close",function() {
// do your close logic
});
function response(info) {
}