NodeJs async sql to variable - node.js

I want a result like this
var rolecheck = ['289773584216358912','281531832938266625'];
Only fetched from a database, so I can compare it to another array with Id's (and yes it's supposed to be a string)
The purpoose of this is to check, before executing a command, if the user has a specific role with permission for that role. So it needs to be a function able to be called.
I've never worked with NodeJs async functions, so i have no clue how to convert this sql to an array:
The content of the .then is just some code of me trying to find out how it works, so ignore the consolelogs etc. Note: the logs do return the correct roles, but i just need them to return them to use them in my compare function.
sql.all("SELECT roleId FROM roles WHERE punish = 'true' and guildId = '"+guildids+"'").then(row => {
if (row) {
var rolecheck = [];
row.forEach(function(row){
rolecheck.push(row.roleId);
});
console.log(rolecheck);
}
});
returning does not work, so I need a workaround.
Here's where i compare it: (this works fine as long as rolecheck and role.id are defined correctly, which they aren't. It does work when i hardcode the rolecheck array.
member.forEach(function(role){
if(HasRole(rolecheck, role.id)){
console.log('user has role: '+role.name);
return true;
}
});

In case you can use a node version with async/await support, like version 7, here is a way to write promise-based code in a synchronous manner. This makes it simpler to pass around the row value.
async function myFunction () {
try {
let member = ''; // whatever member should be
let row = await sql.all("SELECT roleId FROM roles WHERE punish = 'true' and guildId = '"+guildids+"'");
// now you have the row available, outside of 'then' blocks
var rolecheck = [];
if (row) {
row.forEach(function(row){
rolecheck.push(row.roleId);
});
console.log(rolecheck);
}
member.forEach(function(role){
if (HasRole(rolecheck, role.id)) {
console.log('user has role: '+role.name);
return true;
}
});
} catch (error) {
consol.log(error.stack);
}
}
If sql.all did not return a promise you could instead do something like this which would also work with callback based functions
async function myFunction () {
try {
let row = await runSql();
// everything else same as first example above
}
async function runSql () {
try {
return new Promise(function (resolve, reject) {
sql.all("SELECT roleId FROM roles WHERE punish = 'true' and guildId = '"+guildids+"'")
.then(row => {
if (row) {
var rolecheck = [];
row.forEach(function(row){
rolecheck.push(row.roleId);
});
console.log(rolecheck);
resolve(row);
} else {
reject('Row not found');
}
});
});
} catch (error) {
console.log('erro')
}
};

Related

Async/await function is returning "undefined" instead of expected String value

I am trying to learn how to work with async functions. I have my code below. There are two functions. The first, apiAddTask, calls the second function SchedulesDAO.GetScheduleByTitle. Both functions are static async functions and 'await' is used when calling the GetScheduleByTitle function.
In the GetScheduleByTitle function, I grab the needed ID from a Mongo Database. The function is correctly grabbing the ID, but is not returning it. Instead it returns undefined.
apiAddTask function:
static async apiAddTask(req, res, next){
try{
let taskFrequency = null
const taskTitle = req.body.title
const taskRepeat = req.body.repeat
if (taskRepeat == true){
taskFrequency = req.body.frequency
}
const taskStart = req.body.startdate
const taskEnd = req.body.enddate
let taskSchedule = await SchedulesDAO.GetScheduleByTitle(req.body.schedule) //CALL TO GETSCHEDULESBYTITLE <--
console.log(taskSchedule) // THIS PRINTS UNDEFINED <------------
const addResponse = await SchedulesDAO.addTask(
taskTitle, taskRepeat, taskFrequency, taskStart, taskEnd, taskSchedule
)
res.json({status:"success"})
}catch(e){
res.status(500).json({error: e.message})
}
}
GetScheduleByTitle Function:
static async GetScheduleByTitle(title) {
let query = {title: title}
let idString = ""
let cursor
try{
cursor = await schedules.find(query)
}catch (e) {
console.error("Unable to issue find command: " + e)
return {schedule: []}
}
cursor.toArray(function(err, results){
console.log(results[0]._id.toString()) //THIS PRINTS THE RIGHT ID STRING <-------------
idString = results[0]._id.toString()
return idString
})
}
I haven't been able to figure out exactly what I am missing. Please let me know if it would be helpful to see any other code.
Most of your code is fine. The issue lies in the fact that you are using a callback within your GetScheduleByTitle function:
async function () {
cursor.toArray(function(err, results){
console.log(results[0]._id.toString()) //THIS PRINTS THE RIGHT ID STRING <-------------
idString = results[0]._id.toString()
return idString
})
// implicit return undefined
}
What is happening is that your cursor.toArray() is being called, as you expect, but it is then continuing execution onto the implicit undefined return. Your function returns undefined before any of the callback logic is even processed.
The way to fix this:
try {
const results = await cursor.toArray()
let idString = results[0]._id.toString()
return idString
} catch (err) {
console.trace(err)
throw new Error(err)
}
Use the asynchronous promise based method.
If one does not exist, you could create a wrapper for a callback only function like so:
const cursorToArray = async (cursor) => {
return new Promise((resolve, reject) => {
cursor.toArray(function(err, results){
if (results)
resolve(results)
else
reject(err)
})
})
}

How to call a custom function synchronously in node js?

I am developing a server project which needs to call some functions synchronously. Currently I am calling it in asynchronous nature. I found some similar questions on StackOverflow and I can't understand how to apply those solutions to my code. Yet I tried using async/await and ended up with an error The 'await' operator can only be used in an 'async' function
Here is my implementation
function findSuitableRoom(_lecturer, _sessionDay, _sessionTime, _sessionDuration, _sessionType){
let assignedRoom = selectRoomByLevel(preferredRooms, lecturer.level, _sessionDuration); <------- Need to be call synchronously
if (!assignedRoom.success){
let rooms = getRooms(_sessionType); <------- Need to be call synchronously
assignedRoom = assignRoom(_lecturer.rooms, _sessionDuration, _lecturer.level);
} else {
arr_RemovedSessions.push(assignedRoom.removed)
}
return assignedRoom;
}
function getRooms(type){
switch (type){
case 'Tutorial' : type = 'Lecture hall'
break;
case 'Practical' : type = 'Lab'
break;
default : type = 'Lecture hall'
}
Rooms.find({type : type},
(err, rooms) => {
if (!err){
console.log('retrieved rooms ' + rooms)
return rooms;
}
})
}
Here I have provided only two methods because full implementation is very long and I feel if I could understand how to apply synchronous way to one method, I can manage the rest of the methods. Can someone please help me?
Well yes await is only available inside an async function so put async infront of findSuitableRoom.
Also you did a classic mistake. You use return inside of a callback function, and expect getRooms to return you some value.
async function findSuitableRoom(
_lecturer,
_sessionDay,
_sessionTime,
_sessionDuration,
_sessionType
) {
let assignedRoom = selectRoomByLevel(
preferredRooms,
lecturer.level,
_sessionDuration
);
if (!assignedRoom.success) {
try {
let rooms = await getRooms(_sessionType);
} catch (err) {
console.log("no rooms found");
}
assignedRoom = assignRoom(
_lecturer.rooms,
_sessionDuration,
_lecturer.level
);
} else {
arr_RemovedSessions.push(assignedRoom.removed);
}
return assignedRoom;
}
Also wrap it in an try / catch
Since .find() returns an promise if you dont pass an callback you can write it like this
function getRooms(type) {
switch (type) {
case "Tutorial":
type = "Lecture hall";
break;
case "Practical":
type = "Lab";
break;
default:
type = "Lecture hall";
}
return Rooms.find({ type });
}
Note here findSuitableRoom is no longer synchronouse. Its async and returns an promise. That means you will need to use the function like this:
findSuitableRoom.then(res => { console.log(res); })
The 'await' operator can only be used in an 'async' function
This means whenever you want to use the await keyword it needs to be inside a function which has an async keyword (returns promise)
read this https://javascript.info/async-await for more info
const add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => { //to make it asynchronous
if (a < 0 || b < 0) {
return reject("don't need negative");
}
resolve(a + b);
}, 2000);
});
};
const jatin = async () => {
try{
const sum = await add(10, 5);
const sum2 = await add(sum, -100);
const sum3 = await add(sum2, 1000);
return sum3;
} catch (e) {
console.log(e);
}
};
jatin()
Try this
let's understand this
add is a normal function that does some asynchronous action like
waiting for 2 seconds
normally we use await with async function so in order to use it we make as async function jatin and use await with add function call
to make it synchronous, so until first await add call() doesn't
happen it wont execute another await add call().
Example code if you will use in your app.js
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateToken();
res.status(200).send({
user,
token,
});
}
catch (error) {
console.log(error);
res.status(400).send();
}
});

await not working for insert command in nodejs

I have an array of addons and I want to insert them into the db table.
var addons = [sample,sample,.....]
return new Promise((resolve,reject) => {
addons.foEach(async addon => {
// first check if the items is in db
const response = await Kinex.where({}).from('table_name');
if(response.length == 0){
// insert new record
const insertResp = kinex('table_name').insert(addon)
addon.system_id = insertResp[0];
}else{
addon.system_id = response[0].id;
}
})
})
What I expected is to have unique record in the database, but the above code produced duplicate record in the database. Please help to find out the issue with the code.
The problem is running async function inside a loop. As mentioned by #Felix, forEach doesn't know about async functions and doesn't wait for your where query to return. If you wanna do things in async manner inside loops, you can do it with for..of loops. Also make sure to always use try/catch blocks while using async/await. Below is the code in your case:
const addons = [sample,sample,.....];
return new Promise(async (resolve, reject) => {
try {
for (let addon of addons) {
// first check if the items is in db
const response = await Kinex.where({}).from('table_name');
if (response.length) {
const insertResp = await kinex('table_name').insert(addon)
addon.system_id = insertResp[0];
} else addon.system_id = response[0].id;
resolve(); // resolve with whatever you wants to return
}
} catch (e) {
reject(e)
}
});
You can read more on for..of with async/await here.
As pointed by #Sándor, here's the code using Promise.all:
var addons = [sample, sample, .....]
return Promise.all(addons.map(async addon => {
// Do your async stuff here
// first check if the items is in db
const response = await Kinex.where({}).from('table_name');
if (response.length == 0) {
// insert new record
const insertResp = kinex('table_name').insert(addon)
addon.system_id = insertResp[0];
} else {
addon.system_id = response[0].id;
}
}))

Break from async function in await call

Okay, so I'm working with data from Memcache using a promise based library but the issue I'm having is I don't know a way to break from the async call if a result is found?
The code I'm working with is:
const _pong = function() {
return socket.emit('aye', {
pong: globals.uuid()
});
};
return socket.on('helo', async function(data) {
socket._uuid = data.uuid;
let key = 'ws-ping:' + data.uuid;
await cache.get(key).then((result) => {
if(result !== undefined) {
_pong();
}
});
......
});
I basically need to just ignore the rest of the socket.on function if a result is found using the given key? but it seems to continue?
Because you're using await, you can ditch the .then, and get the result directly, in the same block - if the result exists, then just return (after _ponging, if that's the logic you're looking for):
return socket.on('helo', async function(data) {
socket._uuid = data.uuid;
let key = 'ws-ping:' + data.uuid;
const result = await cache.get(key);
if (result !== undefined) {
_pong();
return;
}
// ...
});

Async/Await not waiting

I'm running into an issue which I don't fully understand. I feel like there are likely concepts which I haven't grasped, code that could be optimized, and possibly a bug thrown in for good measure.
To greatly simplify the overall flow:
A request is made to an external API
The returned JSON object is parsed and scanned for link references
If any link references are found, additional requests are made to populate/replace link references with real JSON data
Once all link references have been replaced, the original request is returned and used to build content
Here, is the original request (#1):
await Store.get(Constants.Contentful.ENTRY, Contentful[page.file])
Store.get is represented by:
async get(type, id) {
return await this._get(type, id);
}
Which calls:
_get(type, id) {
return new Promise(async (resolve, reject) => {
var data = _json[id] = _json[id] || await this._api(type, id);
console.log(data)
if(isAsset(data)) {
resolve(data);
} else if(isEntry(data)) {
await this._scan(data);
resolve(data);
} else {
const error = 'Response is not entry/asset.';
console.log(error);
reject(error);
}
});
}
The API call is:
_api(type, id) {
return new Promise((resolve, reject) => {
Request('http://cdn.contentful.com/spaces/' + Constants.Contentful.SPACE + '/' + (!type || type === Constants.Contentful.ENTRY ? 'entries' : 'assets') + '/' + id + '?access_token=' + Constants.Contentful.PRODUCTION_TOKEN, (error, response, data) => {
if(error) {
console.log(error);
reject(error);
} else {
data = JSON.parse(data);
if(data.sys.type === Constants.Contentful.ERROR) {
console.log(data);
reject(data);
} else {
resolve(data);
}
}
});
});
}
When an entry is returned, it is scanned:
_scan(data) {
return new Promise((resolve, reject) => {
if(data && data.fields) {
const keys = Object.keys(data.fields);
keys.forEach(async (key, i) => {
var val = data.fields[key];
if(isLink(val)) {
var child = await this._get(val.sys.linkType.toUpperCase(), val.sys.id);
this._inject(data.fields, key, undefined, child);
} else if(isLinkArray(val)) {
var children = await* val.map(async (link) => await this._get(link.sys.linkType.toUpperCase(), link.sys.id));
children.forEach((child, index) => {
this._inject(data.fields, key, index, child);
});
} else {
await new Promise((resolve) => setTimeout(resolve, 0));
}
if(i === keys.length - 1) {
resolve();
}
});
} else {
const error = 'Required data is unavailable.';
console.log(error);
reject(error);
}
});
}
If link references are found, additional requests are made and then the resulting JSON is injected into the original JSON in place of the reference:
_inject(fields, key, index, data) {
if(isNaN(index)) {
fields[key] = data;
} else {
fields[key][index] = data;
}
}
Notice, I'm using async, await, and Promise's I believe in their intended manor. What ends up happening: The calls for referenced data (gets resulting of _scan) end up occurring after the original request is returned. This ends up providing incomplete data to the content template.
Additional information concerning my build setup:
npm#2.14.2
node#4.0.0
webpack#1.12.2
babel#5.8.34
babel-loader#5.4.0
I believe the issue is in your forEach call in _scan. For reference, see this passage in Taming the asynchronous beast with ES7:
However, if you try to use an async function, then you will get a more subtle bug:
let docs = [{}, {}, {}];
// WARNING: this won't work
docs.forEach(async function (doc, i) {
await db.post(doc);
console.log(i);
});
console.log('main loop done');
This will compile, but the problem is that this will print out:
main loop done
0
1
2
What's happening is that the main function is exiting early, because the await is actually in the sub-function. Furthermore, this will execute each promise concurrently, which is not what we intended.
The lesson is: be careful when you have any function inside your async function. The await will only pause its parent function, so check that it's doing what you actually think it's doing.
So each iteration of the forEach call is running concurrently; they're not executing one at a time. As soon as the one that matches the criteria i === keys.length - 1 finishes, the promise is resolved and _scan returns, even though other async functions called via forEach are still executing.
You would need to either change the forEach to a map to return an array of promises, which you can then await* from _scan (if you want to execute them all concurrently and then call something when they're all done), or execute them one-at-a-time if you want them to execute in sequence.
As a side note, if I'm reading them right, some of your async functions can be simplified a bit; remember that, while awaiting an async function call returns a value, simply calling it returns another promise, and returning a value from an async function is the same as returning a promise that resolves to that value in a non-async function. So, for example, _get can be:
async _get(type, id) {
var data = _json[id] = _json[id] || await this._api(type, id);
console.log(data)
if (isAsset(data)) {
return data;
} else if (isEntry(data)) {
await this._scan(data);
return data;
} else {
const error = 'Response is not entry/asset.';
console.log(error);
throw error;
}
}
Similarly, _scan could be (assuming you want the forEach bodies to execute concurrently):
async _scan(data) {
if (data && data.fields) {
const keys = Object.keys(data.fields);
const promises = keys.map(async (key, i) => {
var val = data.fields[key];
if (isLink(val)) {
var child = await this._get(val.sys.linkType.toUpperCase(), val.sys.id);
this._inject(data.fields, key, undefined, child);
} else if (isLinkArray(val)) {
var children = await* val.map(async (link) => await this._get(link.sys.linkType.toUpperCase(), link.sys.id));
children.forEach((child, index) => {
this._inject(data.fields, key, index, child);
});
} else {
await new Promise((resolve) => setTimeout(resolve, 0));
}
});
await* promises;
} else {
const error = 'Required data is unavailable.';
console.log(error);
throw error;
}
}

Resources