Problem: I cannot get a variable into the correct scope, using the .get function from the nodejs library 'sqlite3'.
Explanation: Even though I have declared the variable 'image_path' in a scope from which it can be returned out of the function, when I assign it a value within the .get method, the scope of image_path appears to change; It is no longer able to be returned from the function 'returnImagePath' as expected.
Aim: I simply want to create a function with which I can query an Sqlite3 database using nodejs and return a value. If there is a better way of doing that then what I am currently trying, I will accept it as an answer.
Example:
var sqlite3 = require('sqlite3');
var dataBaseFile = 'test.db';
var dataBase = new sqlite3.Database(dataBaseFile);
function returnImagePath(id){
var image_path;
dataBase.serialize(function(){
var statementReturnImagePath = dataBase.prepare(`SELECT image_path FROM images WHERE id = ${id}`);
statementReturnImagePath.run();
statementReturnImagePath.finalize();
dataBase.get(`SELECT image_path FROM images WHERE id = 1`, function(error, row){
image_path = row.image_path;
console.log(image_path); //image_path is defined here, and logs as expected
});
dataBase.close();
});
console.log(image_path); //image_path is undefined here
return image_path;
}
var asdf = returnImagePath(1);
console.log(asdf); //this, therefore doesn't work
In the node.js world, you have to understand what is asynchronous function.
If a process requires IO operations, you cannot just return the data from that process, instead you have to "wait" for the data.
We usually achieve this with a callback function.
Here is the example for you,
var sqlite3 = require('sqlite3');
var dataBaseFile = 'test.db';
var dataBase = new sqlite3.Database(dataBaseFile);
//callback is a function which accept (err, data)
function returnImagePath(id, callback){
dataBase.serialize(function(){
dataBase.get('SELECT image_path FROM images WHERE id = ' + id, function(error, row){
if (error) {
//stop and return error if error occurs
return callback(error, null);
}
image_path = row.image_path;
console.log(image_path); //image_path is defined here, and logs as expected
//now you get the image path and return it with callback
callback(null, image_path);
dataBase.close();
});
});
}
returnImagePath(1, function(err, image_path) {
if (err) { //capture error(s)
return console.error(err);
}
console.log(image_path); //now you get the image path
});
Related
I want to get the result from the closure function. I did try doing some callback functions too but still I'm getting the same issue. I was able to console.log the result inside the closure/callback but can't return the response into a variable. I tried some of the solutions posted already but weren't able to succeed.
Here's my code:
var fcmTokens = [input.fcmToken];
switch(topicType) {
case 'post':
const username = (input.username).toLowerCase();
const userPrefix = Config.get(`setting.topic.user.prefix`);
fcmTokens = Topic.get(`${userPrefix}-${username}`, {AttributesToGet : ['fcmToken']}, function(err,foundTopic) {
var result = foundTopic.attrs.fcmToken;
console.log(result) //Able to log my expected result
return result;
})
console.log(fcmTokens) //undefined
break;
}
I was able to resolve my own issue by installing a library called promisified-vogels as my Model is using a DynamoDB.
From my current code above to this:
let userTopic = await Topic
.getAsync(`${userPrefix}-${username}`,{ConsistentRead: true, AttributesToGet : ['fcmToken']}) //from original function "get" changed to "getAsync"
.then(function(user){
return user.attrs.fcmToken;
})
.catch(function(err){
console.log(err)
});
fcmTokens = userTopic; // i was able to get the list of record im expecting.
Reference Library : https://github.com/servel333/vogels-promisified
**I want to return the data key values but it is not working. Please help me. The main concept of this method is when this is invoke dal is the keyword and it fetches that key values from the dynamodb. For that i used global also but not updating that global variable also.
I tried returning the value from the callback, as well as assigning the result to a local variable inside the function and returning that one, but none of those ways actually return the response (they all return undefined or whatever the initial value of the variable result is).**
function getLocation(){
let a=[];
const documentClient = new AWSS.DynamoDB.DocumentClient({region:"us-east-2"});
const params = {
TableName : "ProductLocation1",
Key :{
Product_Name : 'dal'
}
};
documentClient.get(params,(err,data)=>{
if(err){
console.log('error is',err);
}
console.log('data is : ',data);
global.a=Object.keys(data);
});
return global.a;
}
try {
const result = await documentClient.get(params).promise();
} catch (error) {
// handle error
}
You could get the result using the promise & await rather than the callback function. In this way, you don't need to store them in a local variable.
Little info, i have an arp.js file which takes a subnet address "192.168.2" and gets all strings returned from arp -a and stores in an array.
I can't figure out why my arpList function is returning an undefined value in my index.js file.
All the console.logs are returning the correct values in the arp.js page when called from the index.js, but the ipObj is coming up undefined. Even the console.log before i return of ipObj works.
Any help would be greatly appreciated.
var { spawn } = require('child_process');
const arpLs = spawn('arp', ['-a']);
var bufferData;
module.exports = {
arpList: function (subnet) {
arpLs.stdout.on('data', data => {
bufferData += data
})
arpLs.stderr.on('data', data => {
console.log('error: ' + data);
});
arpLs.on('exit', function (code) {
if (code != 0) {
console.log("Error exiting"); //if error occurs
}
console.log("exit start 1"); // checking internal processes at stages
var dataArray = bufferData.split(' ');
var ipArray = [];
for (i = 0; i < dataArray.length; i++) {
if (dataArray[i].includes(subnet)) {
ipArray.push(dataArray[i]);
console.log("loop working");
}
}
var ipObj = { "lanIps": ipArray };
console.log("Object is there: "+ipObj)
return ipObj; // this obj should be returned to the index.js call using
})
},
sayMyName: function () {
return "Hello";
}
}
//arpList(ipSubnet);
//INDEX.js
//the index page looks like this
//var arp = require('./arp.js);
//var ipSubnet = "192.168.2";
//var lanIps = arp.arpList(ipSubnet);
//console.log(lanIps);
I ended up adding a callback function to arpList - function (subnet, callback)
Then instead of returning the value pass it into the callback
Then on the index.js side instead of
var lanIps = arp.arpList(value)
i used
arp.arpList(value, function(res){lanIps = res}
return ipObj; // this obj should be returned to the index.js call using
It won't be returned. The reference say nothing about return value. Node-style callbacks rarely work like that because they are potentially asynchronous and returned value cannot be taken into account.
This a special case of this well-known problem. The process is asynchronous and is finished after arp.arpList(ipSubnet) call, there's nothing to assign to lanIps. This is a use case for promises. There are already third-party promisified counterparts like child-process-promise.
The problem can be also solved by moving to synchronous API. child_process functions have synchronous counterparts, including spawnSync.
I am trying to refactory my nodejs server using promises with Bluebird library, but I am stuck in a simple problem.
After to get the users from my db, I want to list all notification class associated with this user:
Bad Way (working...)
adapter.getUsers(function(users){
users.rows.forEach(function(item){
user = item.username;
adapter.getNotifications(user, function(notificationList){
console.log(notificationList);
})
});
});
Elegant Tentative Way (not working...)
var getNotifications = Promise.promisify(adapter.getNotifications);
adapter.getUsers().then(function(users) {
users.rows.forEach(function(item){
var dbUser = "sigalei/" + item.value.name;
console.log(dbUser);
return getNotifications(dbUser);
});
}).then(function(result){
console.log(result);
console.log("NOTIFICATIONLIST");
});
However when I execute this code I get this error inside my getNotification method:
Unhandled rejection TypeError: Cannot read property 'nano' of undefined
at Adapter.getNotifications (/Users/DaniloOliveira/Workspace/sigalei-api/api/tools/couchdb-adapter.js:387:30)
at tryCatcher (/Users/DaniloOliveira/Workspace/sigalei-api/node_modules/bluebird/js/main/util.js:26:23)
EDIT
After the user2864740`s precious comments, I noticed that the error is related with some scope problem. So, why after to use promisify method, the method dont getNotifications recognize the "this" env variable?
var Adapter = module.exports = function(config) {
this.nano = require('nano')({
url: url,
request_defaults: config.request_defaults
});
};
Adapter.prototype.getNotifications = function(userDb, done) {
var that = this;
console.log(that);
var userDbInstance = that.nano.use(userDb);
userDbInstance.view('_notificacao', 'lista',
{start_key: "[false]", end_key: "[false,{}]"},
function(err, body) {
if(err){ done(err); }
done(body);
});
};
This is just the very common problem of calling "unbound" methods.
You can pass the context as an option to Promise.promisify to have it bound:
var getNotifications = Promise.promisify(adapter.getNotifications, {context: adapter});
Alternatively, you'd need to .bind() the method, or call the new getNotifications function on the adapter (using .call()). You might also consider using Promise.promisifyAll(adapater) and then just calling adapter.getNotificationsAsync(…).
Notice that this still doesn't work. You cannot simply create promises in a loop - you need to await them explicitly and return a promise from the then callback, otherwise just the undefined value you returned will be passed to the next callback immediately.
adapter.getUsers().then(function(users) {
return Promise.all(users.rows.map(function(item){
var dbUser = "sigalei/" + item.value.name;
console.log(dbUser);
return getNotifications(dbUser);
}));
}).then(function(results) {
for (var i=0; i<results.length; i++)
console.log("result:", results[i]);
});
Instead of Promise.all(users.rows.map(…)), in Bluebird you can also use Promise.map(users.rows, …).
What about simply
var getNotifications = Promise.promisify(adapter.getNotifications.bind(adapter));
or possibly
var getNotifications = Promise.promisify(function () {
return adapter.getNotifications.apply(adapter, arguments);
});
?
I'm not sure I understand your problem well, but this should make sure this is bound and not undefined when you do return getNotifications(dbUser);
I wrote a module called accountManager.js
var sqlite3 = require('sqlite3');
var db = new sqlite3.Database("./users.db");
exports.userExists = function userExists(nickName) {
var stmt = 'SELECT * FROM users WHERE login="' + nickName + '"';
db.each(stmt,function(err,row) {
if(row) {
if(row.login==nickName) return true;
else return false;
}
});
}
In my main app.js file I've got
var accountManager = require('./lib/accountManager');
console.log(accountManager.userExists('user1'));
This app says 'undefined' in console... I checked that module is working fine, I guess it's problem with callback? Please, give me some help, I don't understand what is wrong with this code...
You need to understand how asynchronous functions and callbacks work.
Basically you cannot return anything inside the callback but need to invoke another callback which you pass to userExists.
var sqlite3 = require('sqlite3');
var db = new sqlite3.Database("./users.db");
exports.userExists = function userExists(nickName, cb) {
var stmt = 'SELECT * FROM users WHERE login="' + nickName + '"';
db.each(stmt,function(err,row) {
if(row) {
cb(row.login == nickName);
}
});
}
To use it:
accountManager.userExists('user1', function(found) {
console.log(found);
});
Besides that, your code has a gaping SQL injection hole and might not do what you intend to do. Here's a fixed version of the userExists function:
exports.userExists = function userExists(nickName, cb) {
var stmt = 'SELECT COUNT(*) AS cnt FROM users WHERE login = ?';
db.get(stmt, nickName, function(err, row) {
cb(row.cnt > 0);
});
};
Why is this better?
You do not interpolate the value in the SQL string (which is bad, you would have to escape stuff to avoid SQL injection). Passing it separately is much cleaner and better
You just want to know if a user exists. So retrieve the count (which will be exactly one row). If it's not zero the user exists.
Now the callback is always invoked. In the first example that is more closely based on your code it would only be invoked in case a user has been found - most likely not what you wanted.
You're returning a value from within the callback from db.each. However, this value is not returned by the outer function (userExists), which may return before the function passed to db.each is ever called.
You may want to provide a callback to the userExists function, like so:
exports.userExists = function (nickName, cb) {
var stmt = 'SELECT * FROM users WHERE login="' + nickName + '"';
var found=false;
db.each(stmt,function(err,row) {
if(row) {
if(row.login==nickName) {
found=true;
cb(true);
}
}
}, function () {
if (!found) {
cb(false);
}
});
}
Then, call it like:
var accountManager = require('./lib/accountManager');
accountManager.userExists('user1', function (found) {
console.log(found);
});