Variable not changing in function Javascript - javascript

I have written some code where I have a variable that changes if I enter an if statement inside the function of an sql query. However the function does not change globally only locally. The code is the following:
app.post("/energy/api/Admin/users", function(req,res)
var correct=0
sql.query(`SELECT apikey,privileges FROM users WHERE apikey=?`,[req.user.apikey],(err,res1) => {
if (err) {
console.log("error: ", err);
result(err, null);
return;
}
else if (res1.length && res1[0].privileges=="superuser"){
console.log("Apikey correct");
correct=1;
}
else correct=0;
});
console.log(correct);
if (correct==1){
....
}
Note: The if (correct==1) never happens even if correct is set to 1 inside the query function. I think this is not supposed to happen with variables in javascript. Why does this happen?

Change it it promise style as you are trying to access the value before even it is set. the code will look like this
app.post("/energy/api/Admin/users", async function (req, res) {
const correct = await getResults(req.user.apikey);
console.log(correct);
if (correct == 1) {
}
});
function getResults(key) {
return new Promise((resolve, reject) => {
sql.query("SELECT apikey,privileges FROM users WHERE apikey=?", [key], (err, res1) => {
if (err) {
console.log("error: ", err);
reject(err);
} else if (res1.length && res1[0].privileges == "superuser") {
console.log("Apikey correct");
resolve(1);
} else {
resolve(1);
}
});
});
}

The query function is indeed asynchronus. The easy way is to work with async await which handles the promise responses. You could try this:
app.post("/energy/api/Admin/users", async function(req,res)
var correct=0
let response = await sql.query(`SELECT apikey,privileges FROM users WHERE apikey=?`,[req.user.apikey]);
// then here you can check for the response if its an error and so on
if (response) {
console.log("response: ", response);
//... your code now you can use the response without thiunking of asynchronity

Related

using async with mysql

So i got the following code. I want to check if Name already exists in my database. If not I want to insert it. And it basically works with my code. My only problem is, that the last bit of code where it checks whether "success" is true gets executed before it even checked the database. (when using a Name that doesn't exist in the database my console is like this:
Name is already taken <--- this piece of code should be executed last. Then that would not appear
No Duplicate, insert it
starting insert function
I've heard of async and await but I just could not figure it out. I could really need some help.
Here is my Code:
app.post("/api/createProject", (req, res) => {
var success = false;
// console.log(req.body);
const Name = req.body.Name;
const Status = req.body.Status;
const Deadline = req.body.Deadline;
const EstimatedHours = req.body.EstimatedHours;
const BudgetHours = req.body.BudgetHours;
const BudgetCost = req.body.BudgetCost;
const Description = req.body.Description;
// check if Projct Name is already taken
const SEARCH_NAME = `SELECT ID FROM projects WHERE Name = '${Name}'`;
db.query(SEARCH_NAME, (err, results) => {
if (err) {
return err;
} else {
if ((results.length < 1)) {
console.log("No Duplicate, insert it")
insertDatabase();
}
}
});
// will only get executed when no Errors got detected above (Name is not already taken)
function insertDatabase() {
console.log("starting insert function");
const CREATE_NEW_PROJECT = "INSERT INTO projects (Name, Status, Deadline, EstimatedHours, BudgetHours, BudgetCost, Description) VALUES (?,?,?,?,?,?,?)";
db.query(
CREATE_NEW_PROJECT,
[
Name,
Status,
Deadline,
EstimatedHours,
BudgetHours,
BudgetCost,
Description,
],
(err, result) => {
if (!err) {
success = true;
}
}
);
}
if (success == true){
return 0;
} else {
console.log("Name is already taken");
return "Name is already taken";
}
});
Your issue is to do with the asynchronous operation of NodeJS. What is happening in your code is all of your functions are getting "fired" because the code will execute sequentially, but it does not wait for the functions to return.
This is why your if / else statement is getting executed as the "else" - the query hasn't returned to set the "success" to true and therefore it hits the else statement because of its default value.
So we need to refactor your code a bit to make it suitable for async-await. The async-await will wait for a function to return before carrying on to the next statement. With the async-await command, the function it is calling must return a promise.
First we abstract your query into a function with a promise. A promise will run your query and only return when your query is complete:
async function Query (request) {
return new Promise ((resolve, reject) => {
const SEARCH_NAME = `SELECT ID FROM projects WHERE Name = '${request.Name}'`;
db.query(SEARCH_NAME, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results)
}
});
})
}
The above function takes request, which would be your req.body as a parameter, carries out the query and returns the results.
We also need to refactor your insertDatabase function into async-await to see if our insert was successful or not (as this seems to be what you want to know):
async function insertDatabase(request) {
return new Promise((resolve, reject) => {
console.log("starting insert function");
const CREATE_NEW_PROJECT = "INSERT INTO projects (Name, Status, Deadline, EstimatedHours,
BudgetHours, BudgetCost, Description) VALUES (?,?,?,?,?,?,?)";
db.query(
CREATE_NEW_PROJECT,
[
request.Name,
request.Status,
request.Deadline,
request.EstimatedHours,
request.BudgetHours,
request.BudgetCost,
request.Description,
],
(err) => {
if (err) {
reject(false)
}
else {
resolve(true)
}
});
});
}
We then check the results for length less than 1:
var result = await Query(req.body);
if (result <1) {
console.log("No Duplicate, insert it")
success = await insertDatabase(req.body);
}
Putting it all together our code will look something like:
app.post("/api/createProject", async (req, res) => {
var success = false;
var result = await Query(req.body);
if (result <1) {
console.log("No Duplicate, insert it")
success = await insertDatabase(req.body);
if (success == true){
return 0;
} else {
console.log("Name is already taken");
return "Name is already taken";
}
}
}
async function Query (request) {
return new Promise ((resolve, reject) => {
const SEARCH_NAME = `SELECT ID FROM projects WHERE Name = '${request.Name}'`;
db.query(SEARCH_NAME, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results)
}
});
})
}
async function insertDatabase(request) {
return new Promise((resolve, reject) => {
console.log("starting insert function");
const CREATE_NEW_PROJECT = "INSERT INTO projects (Name, Status, Deadline, EstimatedHours,
BudgetHours, BudgetCost, Description) VALUES (?,?,?,?,?,?,?)";
db.query(
CREATE_NEW_PROJECT,
[
request.Name,
request.Status,
request.Deadline,
request.EstimatedHours,
request.BudgetHours,
request.BudgetCost,
request.Description,
],
(err) => {
if (err) {
reject(false)
}
else {
resolve(true)
}
});
});
}
Notice our post callback has "async" in front of it. This is a required keyword in any function that contains the await command.
Additional:
I've left your code mostly how it was with a bit of refactoring, some extra code best practice notes for you:
Multiple return paths is a very bad idea as this leads to testing issues as well as later debugging nightmares, so I would suggest you refactor the return from the final if statement - maybe assign the variables in the if statement but return the variable at the end.
You shouldn't return different types from the same function. So either return 1 or 0 (true or false) on your result, or return two different sets of text. Do not return 0 for one case and text for another, this is very bad practice.
I've also short-cutted by passing your req.body into the functions which probably passes more data than needed. If you don't want to pass this, as a minimum you should encapsulate your assignments that go together into a JSON object:
var request = {
"Name" : req.body.Name,
"Status" : req.body.Status,
"Deadline" : req.body.Deadline,
"EstimatedHours" : req.body.EstimatedHours,
"BudgetHours" : req.body.BudgetHours,
"BudgetCost" : req.body.BudgetCost,
"Description" : req.body.Description
}
and then pass this variable request into the functions instead of req.body.
i'm also relatively new to this, but have an idea.
Try this:
make your function async
async function insertDatabase()
then await your query
await db.query()

Mongoose function logs correct data but using async function to get it returns undefined

I am trying to query some data from a database and return it to a function. The issue I am having is that when I log the results from the query it is working but when I try to query what the function is returning I am getting undefined. It could be that my async function with mongoose isn't exactly set up correctly. Or maybe I just have the wrong idea of what is happening as I am new to the idea of asynchronous programming.
async function returnBlogThumbnails(filter = "recent", callback){
console.log("returning blogs")
//For now simply filter by most recent
if(filter === "recent"){
Blog.find({}).sort('-date').exec((err,docs) => {
return docs;
});
}
}
and the function that calls this function
app.get('/', (req, res)=> {
console.log("go home");
//Call out to query the database then use async function to return
database.returnBlogThumbnails().then((blogs) => {
console.log(blogs);
//res.render('home', blogs);
});
});
As I have said the console.log spits out what I am looking for. However, the function calling the function with the query always shows undefined. Thanks
The problem is you defined your function with callback and trying to call it as a promise. Your function with promise should be:
async function returnBlogThumbnails(filter = "recent") {
return new Promise((resolve, reject) => { // creates a promise
console.log("returning blogs")
//For now simply filter by most recent
if (filter === "recent") {
Blog.find({}).sort('-date').exec((err, docs) => {
if (err) {
reject(err);
} else {
resolve(docs)
}
});
}
});
}
and then in your route you can call it as:
app.get('/', (req, res) => {
console.log("go home");
//Call out to query the database then use async function to return
database.returnBlogThumbnails()
.then((blogs) => {
console.log(blogs);
// do something with blogs
})
.catch((err) => {
console.log(err);
// handle error
});
});

Node js array dashboard

I'm doing the backend of my app with node js. In this case i'm trying to get the typical dashboard like facebook, instagram,....
Where for one user i'm trying to get the users that he follows. And when i get the array of users following, i find the "recetas" that they have (one user can have more than one). And finally i add all this recetas in an array but the problem is that is returning me empty.
getDashboard = function (req, res) {
var myarray = new Array();
//myarray = [];
User.findById(req.params.id, function (err, user) {
if (!user) {
res.send(404, 'User not found');
}
else {
var a = user.following;
a.forEach(function (current_value) {
Receta.find({ "user_id": current_value._id }, function (err, recetas) {
if (!err) {
recetas.forEach(function (receta) {
myarray.push(receta);
}
} else {
console.log('Error: ' + err);
}
});
})
res.send(myarray);
}
});
};
You are dealing with a common async issue. Receta.find is asynchronous, it is not a blocking operation, so res.send is called before all of your Receta.find calls have completed. You can get around this issue by using Promises, assuming they are available in your version of Node:
var a = user.following;
var promises = a.map(function(current_value) {
return new Promise(function(resolve, reject) {
Receta.find({"user_id":current_value._id}, function (err, recetas) {
if(!err) {
resolve(recetas);
} else {
reject(err);
}
});
});
});
Promise.all(promises).then(function(allData) {
res.send(allData);
}).catch(function(error) {
res.send(error);
});
If native Promises aren't available, you can use a library like Q or bluebird
res.send(myarray); is being called before a.forEach completes due to Receta.find which is I/O.
call res.send only when the loop is finished and recetas returned.

Conditional async function

I have my function getting an email from Gmail. I want to run this function n times or until an email is found.
What is a proper way to do it? I tried: http://caolan.github.io/async/docs.html#retry but without success.
I was following this article how to read emails: https://developers.google.com/gmail/api/quickstart/nodejs
Assuming you have a routine called gmail, which returns a promise which succeeds (fulfills) if an email is found, and otherwise fails (rejects), then:
function get(n) {
return gmail().catch(() => {
if (!n) throw "Too many tries!";
return get(--n);
};
}
Usage:
get(5).then(
mail => console.log(mail.body),
() => console.log("No mail!"));
If for some reason you do not like the recursive style:
function get(n) {
let promise = Promise.reject();
do { promise = promise.catch(gmail); } while (n--);
return promise;
}
If gmail is callback style, then
function get(n, cb) {
gmail(function(err, data) {
if (err)
if (!n) get(--n, cb);
else cb("Too many tries!");
else cb(null, data);
});
}
Or better yet, promisify gmail, either using a library or
function promisify(fn) {
return new Promise((resolve, reject) {
fn(function(data, err) {
if (err) reject(err);
else resolve(data);
});
});
}
and then replace gmail in the first solution with promisify(gmail).

Return Meteor.http results in method

I have a Meteor method that wraps around an http.get. I am trying to return the results from that http.get into the method's return so that I can use the results when I call the method.
I can't make it work though.
Here's my code:
(In shared folder)
Meteor.methods({
getWeather: function(zip) {
console.log('getting weather');
var credentials = {
client_id: "string",
client_secret: "otherstring"
}
var zipcode = zip;
var weatherUrl = "http://api.aerisapi.com/places/postalcodes/" + zipcode + "?client_id=" + credentials.client_id + "&client_secret=" + credentials.client_secret;
weather = Meteor.http.get(weatherUrl, function (error, result) {
if(error) {
console.log('http get FAILED!');
}
else {
console.log('http get SUCCES');
if (result.statusCode === 200) {
console.log('Status code = 200!');
console.log(result.content);
return result.content;
}
}
});
return weather;
}
});
For some reason, this does not return the results even though they exist and the http call works: console.log(result.content); does indeed log the results.
(Client folder)
Meteor.call('getWeather', somezipcode, function(error, results) {
if (error)
return alert(error.reason);
Session.set('weatherResults', results);
});
Of course here, the session variable ends up being empty.
(Note that this part of the code seems to be fine as it returned appropriately if I hard coded the return with some dummy string in the method.)
Help?
In your example Meteor.http.get is executed asynchronously.
See docs:
HTTP.call(method, url [, options] [, asyncCallback])
On the server, this function can be run either synchronously or
asynchronously. If the callback is omitted, it runs synchronously and
the results are returned once the request completes successfully. If
the request was not successful, an error is thrown
Switch to synchronous mode by removing asyncCallback:
try {
var result = HTTP.get( weatherUrl );
var weather = result.content;
} catch(e) {
console.log( "Cannot get weather data...", e );
}
Kuba Wyrobek is correct, but you can also still call HTTP.get asynchronously and use a future to stop the method returning until the get has responded:
var Future = Npm.require('fibers/future');
Meteor.methods({
getWeather: function(zip) {
console.log('getting weather');
var weather = new Future();
var credentials = {
client_id: "string",
client_secret: "otherstring"
}
var zipcode = zip;
var weatherUrl = "http://api.aerisapi.com/places/postalcodes/" + zipcode + "?client_id=" + credentials.client_id + "&client_secret=" + credentials.client_secret;
HTTP.get(weatherUrl, function (error, result) {
if(error) {
console.log('http get FAILED!');
weather.throw(error);
}
else {
console.log('http get SUCCES');
if (result.statusCode === 200) {
console.log('Status code = 200!');
console.log(result.content);
weather.return(result);
}
}
});
weather.wait();
}
});
There's not really much advantage to this method over a synchronous get in this case, but if you're ever doing something on the server which can benefit from something like an HTTP call running asynchronously (and thus not blocking the rest of the code in your method), but you still needs to wait for that call to return before the method can, then this is the right solution. One example would be where you need to execute multiple non-contingent gets, which would all have to wait for each other to return one by one if executed synchronously.
More here.
Sometimes asynchronous calls are preferable. You can use async/await syntax for that, and you need to promisify HTTP.get.
import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
const httpGetAsync = (url, options) =>
new Promise((resolve, reject) => {
HTTP.get(url, options, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
Meteor.methods({
async 'test'({ url, options }) {
try {
const response = await httpGetAsync(url, options);
return response;
} catch (ex) {
throw new Meteor.Error('some-error', 'An error has happened');
}
},
});
Notice that meteor test method is marked as async. This allows using await operator inside it with method calls which return Promise. Code lines following await operators won't be executed until returned promise is resolved. In case the promise is rejected catch block will be executed.

Categories

Resources