I am developing a NodeJS application and encountered following problem:
I am using node module node-rest-client to issue REST requests using an API. I use post method provided by the module for my purpose. I need to fetch the json response and write it to seperate variables and return them.
I have defined returnData variable outside the client.post method. I need to update this returnData variable inside the function which is passed as a parameter to the client.post method.
But I have a scope issue here. Although I try to update the returnData variable inside that function, when execution returns from client.post function, I see that the same values I have set (in this case null, but may be any value) before calling client.post function still persists in the variable.
How can I define scope well so that I can update an outside variable inside the function which I pass as a parameter to another function? Any help would be greatly appreciated.
Following is my Code:
module.exports = function(){
require("../config");
var restClient = require('node-rest-client').Client;
var client = new restClient();
var sessionID = null,
reqID = 1;
var login = function(username, password){
var requestParams = {};
var apiParams = {};
requestParams.jsonrpc = "2.0";
requestParams.method = ZABBIX_API_METHODS.login;
apiParams.user = username;
apiParams.password = password;
requestParams.params = apiParams;
requestParams.id = reqID;
requestParams.auth = null;
var args = {
data: requestParams,
headers:{"Content-Type": "application/json-rpc"} // ask response type to be application/json-rpc
};
var returnData = {};
returnData.status = null;
returnData.data = null
client.post(ZABBIX_API, args, function(resData,rawRes){
if(rawRes.statusCode == 200){
returnData.status = rawRes.statusCode;
returnData.data = resData;
}
else{
returnData.status = rawRes.statusCode;
returnData.data = "Request Failed!";
}
reqID = reqID + 1;
console.log(returnData.data);
});
console.log("SessionID:"+getSessionID());
return returnData;
}
var functions = {
login: login
}
return functions;
}
Thank you.
.post is Async, you should do it like this,
var login = function(username, password, callback){
..................
client.post(ZABBIX_API, args, function(resData,rawRes){
if(rawRes.statusCode == 200){
returnData.status = rawRes.statusCode;
returnData.data = resData;
}
else{
returnData.status = rawRes.statusCode;
returnData.data = "Request Failed!";
}
reqID = reqID + 1;
return {login: returnData};
});
//remove all below return statements
Related
var Client = require('node-rest-client').Client;
var client = new Client();
module.exports = {
getWeatherStatus: function() {
var messageData = "";
client.get("http://api.openweathermap.org/data/2.5/weather?q=Pune&appid=123234234234243242", function (data, response) {
console.log(JSON.parse(data));
messageData=data;
});
//how to set the response of that rest call to this messageData object
return messageData;
}
}
this method getWeatherStatus should return the rest response in json format.
Open for totally different suggestion to implement this kind of scenario.
My basic requirement is to use this REST call response and send to other functions.
In getWeatherStatus you use async function client.get. You need wait result from async function and only than return messageData. For this you can use deasync. Example:
var Client = require('node-rest-client').Client;
var client = new Client();
module.exports = {
getWeatherStatus: function() {
var messageData = "";
client.get("http://api.openweathermap.org/data/2.5/weather?q=Pune&appid=123234234234243242", function (data, response) {
console.log(JSON.parse(data));
messageData=data;
});
//We waiting for data.
while (messageData === "") {
require('deasync').sleep(10);
}
return messageData;
}
}
But, maybe, you should return Promise, not data.
Since get is callback function so you have to put your return messageData
in stead of messageData=data.
Code should be like
var Client = require('node-rest-client').Client;
var client = new Client();
module.exports = {
getWeatherStatus: function() {
var messageData = "";
client.get("http://api.openweathermap.org/data/2.5/weather?q=Pune&appid=123234234234243242", function (data, response) {
return JSON.parse(data);
});
}
}
I think you are facing difficulties to deal with with callbacks,
In the method getWeatherStatus, instead of returning the result, you should pass it to a callback function once the treatment is done.
If really you are required to return, galk.in answer seems to be a possible way.
Otherwise, check this out,
var Client = require('node-rest-client').Client;
var client = new Client();
module.exports = {
getWeatherStatus: function(then) {
var messageData = "";
client.get("/some/url", function (data, response) {
then(err=null, JSON.parse(data));
});
}
}
So you may call getWeatherStatus in such way,
// Somewhere else in your code
getWeatherStatus(function fnCallback(err, jsonData) {
console.log(jsonData) // process data here
})
As suggested, Promise are also a good alternative. but it is still async.
Im trying to catch errors returned from my server and return them at the end of a login function. If it succeeds, no errors will be returned and if it fails the list of errors will be returned. The login code is part of an AngularJS service as below:
angular.module('auth').service('RestfulAuthService',['$http','REST_BASE_URL',function($http,base_url){
var self = this;
//define backend urls
var api_urls = {
login: base_url+'/login/',
}
//hold user credentials to be accessed
var api_user = {
username:null,
email:null,
first_name:null,
last_name:null,
}
var api_user_token = null;
self.user_profile = function(){
return api_user;
}
self.login = function(username,password){
var errors = {none:null};
$http.post(api_urls.login,{username:username,password:password})
.then(function(response){ //success
//extract data
api_user.username = response.data.username;
api_user.email = response.data.email;
api_user.first_name = response.data.first_name;
api_user.last_name = response.data.last_name;
api_user_token = response.data.token;
//Add auth token to headers for all future requests
$http.defaults.headers.common['Authorization'] = api_user_token;
errors = {};
},function(response){ //error
errors = response.data; //return serializer errors
});
return errors;
};
//REST OF SERVICE ...
The value returned however is always {none:null}. In other words, the errors variable hasnt been changed by the success or failure functions. I'm pretty sure this is a scoping issue, but I don't know how to fix it.
It is a scoping issue, but it is more so a synchronization issue. The HTTP request starts and return errors; is executed long before any of the .then code is reached. You have to use a callback to pass the error data.
self.login = function(username,password,callback){
var errors = {none:null};
$http.post(api_urls.login,{username:username,password:password})
.then(function(response){ //success
//extract data
api_user.username = response.data.username;
api_user.email = response.data.email;
api_user.first_name = response.data.first_name;
api_user.last_name = response.data.last_name;
api_user_token = response.data.token;
//Add auth token to headers for all future requests
$http.defaults.headers.common['Authorization'] = api_user_token;
errors = {};
callback(errors);
},function(response){ //error
errors = response.data; //return serializer errors
callback(errors);
});
};
The calling code must call with the callback, and use its argument to access the errors.
self.login(username, password, function(errors) {
// handle errors
});
I have this very simple JavaScript code that should return a Fullname using parameters firstname and lastname All works fine when I alert out the fullname within the function. But I'm struggling how to make the fullname variable accessible from outside the function/instance?
here's my code:
var getName = function () {
this.name = function (firstname, lastname, success) {
var fullName = firstname + " " + lastname;
success(fullName);
};
};
var test = new getName();
test.name("John", "Smith", function (output) {
var fullname = output;
alert(fullname); //this works fine
return fullname; // I need this variable to be accessed from outside this function
});
var myName = fullname; //I need to create the variable here
alert(myName); //this does not work
Here's Fiddle
Thank you very much for your help.
Edit: I'm building an iPad app where I'm using cordova and javascript plugins.
One of the plugin gives me access to the file inside the device. Now, I need to be able to get the path and use it outside the callback so that I can use anywhere in the scope:
Here's the plugin code:
var FileManager = function(){
this.get_path = function(todir,tofilename, success){
fail = (typeof fail == 'undefined')? Log('FileManager','read file fail'): fail;
this.load_file(
todir,
tofilename,
function(fileEntry){
var sPath = fileEntry.toURL();
success(sPath);
},
Log('fail')
);
}
this.load_file = function(dir, file, success, fail, dont_repeat){
if(!dir || dir =='')
{
Log('error','msg')('No file should be created, without a folder, to prevent a mess');
fail();
return;
}
fail = (typeof fail == 'undefined')? Log('FileManager','load file fail'): fail;
var full_file_path = dir+'/'+file;
var object = this;
// get fileSystem
fileSystemSingleton.load(
function(fs){
var dont_repeat_inner = dont_repeat;
// get file handler
console.log(fs.root);
fs.root.getFile(
full_file_path,
{create: true, exclusive: false},
success,
function(error){
if(dont_repeat == true){
Log('FileManager','error')('recurring error, gettingout of here!');
return;
}
// if target folder does not exist, create it
if(error.code == 3){
Log('FileManager','msg')('folder does not exist, creating it');
var a = new DirManager();
a.create_r(
dir,
function(){
Log('FileManager','mesg')('trying to create the file again: '+file);
object.load_file(dir,file,success,fail,true);
},
fail
);
return;
}
fail(error);
}
);
}
);
};
}
and here's how I use it:
var b = new FileManager(); // Initialize a File manager
b.get_path('Documents','data.json',function(path){
myPath = path;
console.log(myPath); //this gives me the path of the file in the console, which works fine
return myPath; //I return the path to be accessed outside
});
var filePath = myPath; //here I need to access the path variable
console.log(filePath)// here, the path is undefined and it does not work
//Since I'm using angular services to retrieve the data from json, I'd like to pass the filePath
//this is a fraction of code for retrieving data:
return $resource('data.json',{}, {'query': {method: 'GET', isArray: false}});
//passing the value does not work
return $resource(filePath,{}, {'query': {method: 'GET', isArray: false}});
//even wrapping resource around instance does not work, it breaks the whole app
b.get_path('Documents','data.json',function(path){
myPath = path;
console.log(myPath); //this gives me the path of the file in the console, which works fine
return $resource(myPath,{}, {'query': {method: 'GET', isArray: false}});
});
Factory services:
'use strict';
angular
.module ('myApp')
.factory('getMeData', function ($resource) {
var b = new FileManager(); // Initialize a File manager
b.get_path('Documents','data.json',function(path){
myPath = path;
return myPath;
});
//below doesn't work when passing path (it is undefined)
return $resource(myPath,{}, {'query': {method: 'GET', isArray: false}});
//when wrapping it around, the app crashes
b.get_path('Documents','data.json',function(path){
myPath = path;
return $resource(myPath,{}, {'query': {method: 'GET', isArray: false}});
});
//none of the solution above work
});
If you have the name function return the result of success then you can use the return from the method.
I.e.
var getName = function () {
this.name = function (firstname, lastname, success) {
var fullName = firstname + " " + lastname;
return success(fullName);
};
};
var test = new getName();
var myName = test.name("John", "Smith", function (output) {
var fullname = output;
alert(fullname); //this works fine
return fullname; // I need this variable to be accessed from outside this function
});
alert(myName);
Define fullname outside the callback.
var getName = function () {
this.name = function (firstname, lastname, success) {
var fullName = firstname + " " + lastname;
success(fullName);
};
};
var test = new getName();
var fullname;
test.name("John", "Smith", function (output) {
fullname = output;
alert(fullname); //this works fine
return fullname; // I need this variable to be accessed from outside this function
});
alert(fullname);
Hope it helps. Fiddle
If async required:
Assuming the actual source of the code is an async one, now confirmed from your comment, you should simply process the result within the callback. The value is simply not available outside of the callback as the result arrives later.
The answers by #Jim and #bestmike007 will not work with an async operation as it returns a value from inside a callback which may occur long after the function is run. (note: #bestmike007 does have an improved answer linked in a comment).
e.g. Look at what happens here: http://fiddle.jshell.net/y1m36w9p/1/ or here http://jsfiddle.net/TrueBlueAussie/opczeuqw/1/
The only way to work with async code is in an async manner. That means you can only handle the result inside the callback:
// Get the fullname asyc and process the result
test.name("John", "Smith", function (fullname) {
alert(fullname); // you can only work with the result in here
});
An alternative is to return a jQuery promise from getName.name(), but the end result will still be a callback to do the work, but this time it would look like this:
test.name("John", "Smith").done(function(fullname){
alert(fullname); // you can only work with the result in here
});
but this is more code and complication for no extra benefit at this time.
For your specific updated example code:
I am not familiar enough with Angular to confirm this, and would need to see how getMeData is called, but you should use another callback as a parameter to the getMeData function you register:
'use strict';
angular
.module ('myApp')
.factory('getMeData', function ($resource, callback) {
var b = new FileManager(); // Initialize a File manager
b.get_path('Documents','data.json',function(path){
callback($resource(path,{}, {'query': {method: 'GET', isArray: false}}));
});
});
If async not required (obsolete option)
If (as a previous contradictory comment below said) this was not mean to to be async, then do not use callbacks at all, just simple functions/methods returning values: http://fiddle.jshell.net/y1m36w9p/2/
e.g.
var getName = function () {
this.name = function (firstname, lastname) {
return firstname + " " + lastname;
};
};
// Create a instance of the getName class
var test = new getName();
// Get the fullname asyc and process the result
var fullname = test.name("John", "Smith");
// Do what you like with the value returned from the function
alert(fullname);
i am trying to call function in other js file using require.all these the parameters
> Login = require("LoginRequest");
Create = require("CreateView");
var params = { username : username.value , password : password.value};
var type ="POST";
var URL = "https://localhost/post_auth.php";
var Result; </li>
and here the call funcion from LoginScreen.js
b2.addEventListener('click',function(e)
{
alert(params.username);
if (username.value != '' && password.value != '')
{
Result=Login.Request(params,type,URL);
}
else
{
// warning alert
alert("Username/Password are required");
}
if (Result.logged == true)
{
alert("Welcome " + Result.name + ", Please answer the following question");
Create();
}
else
{
alert(Result.message);
}
});
when i try to pass the parameters to LoginRequest.
function Request(params,type,url){
var Result;
var loginReq = Titanium.Network.createHTTPClient();
loginReq.open(type,url);
loginReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
//send parameters
loginReq.send(JSON.stringify(params));
loginReq.onload = function()
{
var json = this.responseText;
Result = JSON.parse(json);
alert (Result.logged);
alert (Result.name);
};
return Result;
};
exports.Request = Request;
the calling return undifiend object , where is my wrong here ?
That's because you are making an async call.
when you call loginReq.send() the call will be made and it will continue executing the rest of the code without waiting for the async call to be finished, that's why the function returns undefined.
To fix this you can make a sync call instead of an async call (this is a bad bad bad idea) or you could restructure your code, maybe LoginRequest could return the loginReq instance instead of the result
I'm having an issue where I get an error that says...
"Uncaught TypeError: Object f771b328ab06 has no method 'addLocation'"
I'm really not sure what's causing this. The 'f771b328ab06' is a user ID in the error. I can add a new user and prevent users from being duplicated, but when I try to add their location to the list, I get this error.
Does anybody see what's going wrong? The error occurs in the else statement of the initialize function as well (if the user ID exists, just append the location and do not create a new user). I have some notes in the code, and I'm pretty sure that this is partly due to how I have modified an example provided by another user.
function User(id) {
this.id = id;
this.locations = [];
this.getId = function() {
return this.id;
};
this.addLocation = function(latitude, longitude) {
this.locations[this.locations.length] = new google.maps.LatLng(latitude, longitude);
alert("User ID:" );
};
this.lastLocation = function() {
return this.locations[this.locations.length - 1];
};
this.removeLastLocation = function() {
return this.locations.pop();
};
}
function Users() {
this.users = {};
//this.generateId = function() { //I have omitted this section since I send
//return Math.random(); //an ID from the Android app. This is part of
//}; //the problem.
this.createUser = function(id) {
this.users[id] = new User(id);
return this.users[id];
};
this.getUser = function(id) {
return this.users[id];
};
this.removeUser = function(id) {
var user = this.getUser(id);
delete this.users[id];
return user;
};
}
var users = new Users();
function initialize() {
alert("Start");
$.ajax({
url: 'api.php',
dataType: 'json',
success: function(data){
var user_id = data[0];
var latitude = data[1];
var longitude = data[2];
if (typeof users.users[user_id] === 'undefined') {
users.createUser(user_id);
users.users[user_id] = "1";
user_id.addLocation(latitude, longitude); // this is where the error occurs
}
else {
user_id.addLocation(latitude, longitude); //here too
alert(latitude);
}
}
})
}
setInterval(initialize, 1000);
Since I get the ID from the phone and do not need to generate it here (only receive it), I commented out the part that creates the random ID. In doing this, I had to add a parameter to the createUser method within Users() so that I can pass the ID as an argument from Initialize(). See the changes to createUser below:
Before, with the generated ID (the part where the number is generated is in the above code block with comments):
this.createUser = function() {
var id = this.generateId();
this.users[id] = new User(id);
return this.users[id];
};
After, with the ID passed as an argument:
this.createUser = function(id) {
this.users[id] = new User(id);
return this.users[id];
};
If anyone has any suggestions I would really appreciate it. Thanks!
Here you're getting user_id by :
var user_id = data[0];
So it's a part of the json answer : maybe a string or another dictionnary, this can't be a user object. You should try to update your code in your success function inside the "if" block by :
user = users.createUser(user_id);
//The following line is a non sense for me you put an int inside
//an internal structure of your class that should contain object
//users.users[user_id] = "1";
user.addLocation(latitude, longitude);