returning data from for loop - javascript

i am using socket.io to fetch data about a user using his uid when you run this function
function getUserData(uid){
"use strict"
socket.emit('getUserData',uid, function(callback){
console.log('callback' + callback)
for (var i = 0; i < callback.length; i++) {
var row = callback[i];
var username = row.username;
var about = row.about;
var uid = row.uid;
}
})
return {
username: username,
uid: uid,
// about: about
};
}
and it does this on the server side
socket.on('getUserData',function(uid, callback){
connection.query('SELECT * FROM users WHERE uid = ?', [uid], function(err, rows) {
callback(rows)
})
})
but when i do console.log(getUserData(uid)) i get undefined but i do get the object from the first callback what am i doing wrong here?

The callback from .emit() is asynchronous. That means it happens sometime LATER, long after your getUserData() function has already returned. That means you have to communicate back the result using either a callback or a promise. In addition, it makes no sense that you're trying to iterate an array and return one result. You should either return all the results or pick one particular item from the array as your final value. This resolves with the whole array of data, letting the caller decide which one they want to pick from the array.
Here's how you could do so with a promise:
function getUserData(uid){
"use strict"
return new Promise(resolve => {
socket.emit('getUserData',uid, function(returnData){
console.log('returnData', returnData)
resolve(returnData);
});
});
}
// usage
getUserData(someUID).then(results => {
// use results in here
});

Related

Is there a way to know if a row exists in a mysql table using node.js?

I'm trying to make a function that returns true if it detects a row containing a value in a specific column in nodejs.
I've tried to use the result variable from query() without any success :
let rowexists = (mystring) => {
let exists = false;
let sql = "SELECT EXISTS( SELECT 1 FROM mytable WHERE `mycolumn` = '" + mystring + "')";
connection.query(sql, function(error, result, field){
console.log((result[sql]));
console.log(exists);
exists = (result[sql]);
});
return exists;
}
console.log(rowexists("myvalue"));
Event if there is a row with the value "myvalue" (there is), rowexists() always returns false.
IMPORTANT EDIT:
My problem isn't really the fact that this is async, it's the fact that both
console.log((result[sql]));
and
console.log(exists);
return undefined.
A promise is something that is useful in this type of situation.
The issue that you are having, is that your query hasn't finished running by the time the function returns. So, having a promise returned, we can return the value at a later time.
Side Note: You should be using prepared queries when using an SQL database.
let rowexists = (mystring) => {
// Return a new promise
return new Promise(resolve => {
// Create the sql query (this uses placeholders)
// Hard coded values don't need to be placeholders but just for example:
let sql = "SELECT 1 FROM ?? WHERE ?? = ?";
// Query the database replacing the ?? and ? with actual data
connection.query(sql, ['mytable', 'mycolumn', mystring], function(error, result, field){
// Result will either be undefined or a row.
// Convert it to a boolean and return it.
resolve(!!result)
});
});
}
// Get the data
rowexists("myvalue").then(result => console.log(result))
Self invoking function using async/await:
(async function() {
let exists = await rowexists('myothervalue')
console.log(exists)
// The rest of your related code
})()
If you don't like the then() syntax, you can use async/await. Here are two ways in which you can do so:
Basic function using async/await:
async function test() {
let exists = await rowexists('mythrirdvalue')
console.log(exists)
// The rest of your related code
}
test()

Returning results with callbacks

I'm trying to query a database, then make an array of objects from the results, turn them into a JSON object.
I'm not used to Javascript's asynchronous nature and I'm really confused about how to implement something that needs to work synchronously. I know I probably need to use callbacks, but after looking at many tutorials I'm only left more confused.
This is the code without callbacks:
var foreignTable = (tablename,idArr)=>{
var dataArray = [];
//call a query for each of the ids
var objectToAdd;
for(var id of idArr){
objectToAdd = queryForeignTable(tablename,id);
dataArray.push(objectToAdd);
}
return dataArray;
connection.end();
};
var queryForeignTable = (tablename,id)=>{
connection.query("SELECT * FROM "+tablename+" WHERE id="+id, function (error, results, fields) {
if(error)throw error;
var objectToAddToArray={};
//Go through each field in a result and make the object
for(packet of fields){
var label = packet.name;
objectToAddToArray[label] = results[0][label];
}
return objectToAddToArray;
});
};
var arrayOfDrivers = foreignTable("driver",[1,2]);
outputJson["drive"]=arrayOfDrivers;
console.log(outputJson); // { drive: [ undefined, undefined ] }
I attempted foreignTable(tablename, idArr, callback) with the callback calling queryForeignTable with no luck.
Can someone explain how I can get this piece of code working with callbacks?
A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.
from MDN: Callback function
Callbacks are a way of telling a function what to do next, as in "after you're done, run this function".
For example:
first = function (callback) {
console.log("First!");
callback();
}
second = function () {
console.log("Second!");
}
first(second);
Will produce:
First!
Second!
You can also use anonymous function to product the same result:
first(function() {
console.log("Second!")
});
Regarding the specific example from your question, you are correct that you need to use callbacks a bit differently. Rather than using a return statement in each of your two functions, you'll need to use a callback. connection.query is asynchronously coming back with your results as results, but you can't return them to your queryForeignTable function. Instead, give queryForeignTable a callback function to run. The same idea goes for your foreignTable function.
I'm not connected to your database, obviously, so I stubbed a DB connection and simplified what you're trying to do, but it should look something like this:
// Stubbed DB connection
var connection = {};
connection.query = (id, cb) => {
var results = [
{
id: id,
name: 'Name of ' + id,
},
];
cb(null, results);
};
var foreignTable = (ids, cb) => {
var data = [];
for (var i = 0; i < ids.length; i++) {
queryForeignTable(ids[i], (error, obj) => {
data.push(obj);
if (i == ids.length - 1) {
cb(null, data);
}
});
}
};
var queryForeignTable = (id, cb) => {
connection.query(id, (error, results) => {
if (error) {
cb(error, null);
}
cb(null, results[0]);
});
};
foreignTable([1, 2], (error, data) => {
if (error) {
console.error(error);
}
console.log(data);
});
That produces:
[ { id: 1, name: 'Name of 1' }, { id: 2, name: 'Name of 2' } ]
In essence, when you have an urge to return some value(s) from a function in an asynchronous way, give the function a callback parameter, then invoke that callback with your return values.
You can run the code here: https://repl.it/K0YI/3
When you have an asynchronous call, like connection.query(statement, callback), then whatever you want to do with the results of that call needs to be done within the callback.
Bear in mind that async functions don't return the final value that you generally want (usually they return undefined). Instead of using a return value, you pass a callback as a way of saying, "when you're finished, carry on and do this with the results", aka. continuation-passing style.
One of the challenges in your code is that you're issuing separate queries for each ID, and then aggregating the results into an array of responses. This means you'll need to check when all the queries have completed, and only then proceed to display the final results.
Here's your example, re-written, commented, and simplified. Hopefully this helps to explain how the control flow works.
// Read row IDs 1 and 2 from the "driver" table.
readTable("driver", [1, 2], displayData);
// Print the results.
function displayData (arrayOfDrivers) {
console.log(arrayOfDrivers);
}
// Read all rows matching IDs in `idArray` from `tableName`,
// put results into an array, and finally invoke `callback`.
function readTable (tablename, idArray, callback) {
var resultsArray = [];
// Queue up all the async queries.
for (var id of idArray){
queryTable(table, id, handleResponse);
}
// A query finished, so handle the result.
function handleResponse (error, results, fields) {
if (error) {
throw error;
}
// Add the query result to array of results.
resultsArray.push(results[0]);
// Check if all queries are done.
if (resultsArray.length === idArray.length) {
// Invoke the callback with the resultsArray.
// The callback is in fact the `displayData` function.
callback(resultsArray);
}
}
}
// Execute a query, using the `cb` callback to handle the response.
function queryForeignTable (tablename, id, cb) {
var query = "SELECT * FROM " + tablename + " WHERE id=" + id;
connection.query(query, cb);
}
Note that the handleResponse function is defined within the scope of the readTable function, so it can access the variables in readTables scope, such as resultsArray and callback.
Hope that helps.

javascript api call, send and handle data in specific order

I have this API call where i make sure the data return in the same order i send it. However, i realized thats not really what i want, i want to make sure the data is send and taken care of one at a time.
data[n] has returned before data[n+1] is send.
the reason for this is:
If i do it as seen below, the server still gets it in a random order, and therefor saves the data in my DB in a random order. (or well not random, heavier data gets processed slower)
var promiseArray = [];
for (var i = 0; i < data.length; i++) {
var dataPromise = $http.post('/api/bla/blabla', $httpParamSerializer(data[i]))
.then (function (response) {
//return data for chaining
return response.data;
});
promiseArray.push(dataPromise);
}
$q.all(promiseArray).then(function (dataArray) {
//succes
}).catch (function (errorResponse) {
//error
});
how can i make sure the data is send and processed and returned, one at a time in a smooth way ?
You could do something like this:
var i = -1;
processNextdata();
function processNextdata() {
i++;
if(angular.isUndefined(data[i]))
return;
$http.post('/api/bla/blabla', $httpParamSerializer(data[i]))
.then(processNextdata)
}
Update:
Callback after every result:
var i = -1;
processNextdata();
function processNextdata() {
i++;
if(angular.isUndefined(data[i]))
return;
$http.post('/api/bla/blabla', $httpParamSerializer(data[i]))
.then(function(result) {
// do something with a single result
return processNextdata();
}, errorCallback);
}
Callback after everything is done:
var i = -1, resultData = [];
processNextdata()
.then(function(result) {
console.log(result);
}, errorCallback);
function processNextdata() {
i++;
if(angular.isUndefined(data[i]))
return resultData;
$http.post('/api/bla/blabla', $httpParamSerializer(data[i]))
.then(function(result) {
resultData.push(result.data);
return processNextdata();
}, $q.reject);
}
When using the Promise.all([...]) method, the documentation shows the following:
The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved, or rejects with the reason of the first passed promise that rejects.
What this tells us is that there is no expected order of synchronized operations, but in fact the promises run parallel to one another and can complete in any order.
In your case, there is an expected order that you want your promises to run in, so using Promise.all([...]) won't satisfy your requirements.
What you can do instead is execute individual promises, then if you have some that can run in parallel use the Promise.all([...]) method.
I would create a method that takes a request as an argument, then returns the generated promise:
function request (req) {
return new Promise(function (resolve, reject) {
request({
url: url
, port: <port>
, body: req
, json: <true/false>
, method: '<POST/GET>'
, headers: {
}
}, function (error, response, body) {
if (error) {
reject(error);
} else {
resolve(body);
}
});
});
You can then call this function and store the result:
var response = request(myRequest);
Alternatively, you could create an array of your requests and then call the function:
var requests = [request1, request2, ..., requestN];
var responses = [];
for (var i = 0; i < requests.length; i++) {
responses.push(request(requests[i]));
}

How to assign callback return value to a variable in Mongoose?

I'm using mongoose.find() function to check the user's credentials.
The findUser function will receive, from the .find(), error and data arguments.
I want to store on a variable wether the .find() method has found something that matches the query or not.
Instead, my console.log(logged) is displaying a lot of query information about the MongoDB operation.
var logged = UserModel.find(
{ username: username, password: password}, findUser );
console.log(logged);
var findUser = function (err, data) {
(data.length) ? return true : return false;
};
How can I return a true or false from mongoose.find() method?
Remember that nodejs is async non-io blocking so any database operation will run async and probably your variable won't be assigned as you want.
I like to use promises in my flow control, in my case I use bluebird which is a very cool promise library, you could do something like this (assuming express here):
var BPromise = require('bluebird'),
mongoose = BPromise.promisifyAll(require('mongoose'));
// etc etc load your model here
//---------------------------------
var thiz = this;
thiz.logged = false;
var myPromises = [];
myPromises.push(
UserModel.find({ username: username, password: password}).execAsync().then(function(data){
thiz.logged = (data.length>0);
})
);
//wait for all the promises to complete in case you have more than one
BPromise.all(myPromises).then(function(){
// do whatever you need...
});

Populate an array within an asynchronous function in Node.js

I recently started with Mongoose and Node.js and I have some difficulties in handling the result of an asynchronous Mongoose query. I have a collection called 'Groups' and a group can hold members located in a seperate collection called 'Members'. To accomplish this each group has a field named 'members' which is an array of ObjectId's. In addition each member has a profile which is refered to by an ObjectId. So each member has a field named 'profile' and the profiles are stored in a collection called 'Profiles'.
Now I want to get a list of profiles in a given group. Therefore I wrote this code:
// Function that returns a profile by a given objectId
function getprofile(profileId, profile) {
Profile
.findById(profileId)
.exec(function(err, res) {
if(err) { return null; }
if(!res) { return null; }
return res;
});
}
// Main function that returns a list of all (unique) profiles from members within a group
Group
.findById(req.params.id)
.populate('members')
.exec(function(err, group) {
if(err) { return handleError(res, err); }
var tmpProfiles = [];
var allProfiles = [];
for(var i=0; i<group.members.length; i++) {
var memberProfile = group.members[i].profile;
// Check if the member's profile is already in the array
if(objectIndexOf(tmpProfiles, memberProfile) == -1) {
tmpProfiles.push(memberProfile);
allProfiles.push(getprofile(memberProfile)); // add the profile to the array of profiles
}
}
return res.json(allProfiles); // this returns an empty array
});
The problem is that the 'Profile.findById' function and the 'Group.findById' function are both asynchronous and for this reason the function returns an array with only 'null' values. I know that I can solve this by using the 'Async' library but that is not an option for me so I have to go through the 'callback hell'. Can someone put me in the right direction by solving this problem? I already found some solutions which uses recursive calling of functions but I have no idea how to implement it in this case.
You can take advantage of the fact that mongoose methods return Promises.
Note: you will need to either be using Node >= v0.12 or have a Promise library required in the module.
// Function that returns a profile by a given objectId
function getprofile(profileId, profile) {
return Profile.findById(profileId); // return the Promise of this `findById` call
}
// Main function that returns a list of all (unique) profiles from members within a group
Group
.findById(req.params.id)
.populate('members')
.then(function(group) {
var tmpProfiles = [];
var promises = []; // store all promises from `getprofile` calls
for(var i = 0; i < group.members.length; i++) {
var memberProfile = group.members[i].profile;
// Check if the member's profile is already in the array
if(objectIndexOf(tmpProfiles, memberProfile) == -1) {
tmpProfiles.push(memberProfile);
promises.push(getprofile(memberProfile));
}
}
return Promise.all(promises);
})
.then(function(allProfiles) {
res.json(allProfiles);
})
.catch(function(err) {
//todo: handle error
console.log(err);
});
I support your learning by doing callback hell without caolan/async!
Here's an async answer anyway, to compare to your OP and the promises answer
Group...exec(function(err, group){
async.waterfall([
function(done1){
var tmpUnique = [];
async.filter(group.members, function(member, done2){
var isUnique = (tmpUnique.indexOf(member.profile) === -1);
if(isUnique){ tmpUnique.push(member.profile) };
done2(isUnique);
}, function(err, uniqueMembers){
done1(err, uniqueMembers);
})
},
function(uniqueMembers, done1){
async.map(uniqueMembers, function(member, done2){
getProfile(member.profile, done2);
// rewrite getProfile(id, callback){} to callback(err, profile)
// and maybe rename the key member.profile if its an id to get the real profile?
}, function(err, uniqueProfiles){
done1(err, uniqueProfiles)
});
},
], function(err, uniqueProfiles){
//callback(err, uniqueProfiles) or do something further
})
})

Categories