Node js asynchrounous call - javascript

Everytime adding a new data or updating existing data, new_data and updated_data variables will get increment. But when I try to console log the total count's (new_data and updated_data) at the bottom of the code the result is 0. How do we address this in Node using async ?
Code
let new_data = 0
let updated_data = 0
let vehicles = _.each(results.data, function (value, key) {
let condition = { VIN: value.VIN }
Vehicle.model.findOne(condition, function (err, doc) {
if (doc) {
let condition2 = { VIN: doc.VIN }
Vehicle.model.update(condition2, value, function (err, doc1) {
updated_data += 1
if (doc && typeof doc.log === 'function') {
const data = {
action: 'Update',
category: 'Inventory Import',
message: 'Import Successful',
status: '200'
}
return doc.log(data);
}
})
} else {
Vehicle.model.create(value, function (err, doc2) {
new_data += 1
if (doc2 && typeof doc2.log === 'function') {
const data = {
action: 'Create',
category: 'Inventory Import',
message: 'Import Successful',
status: '200'
}
return doc2.log(data);
}
})
}
})
})
console.log("new datad : ", new_data)
console.log("updated_data : ", updated_data)

You can Use ASYNC/AWAIT for that put that code in an async function and then wait for each query for that.I used for of loop because it is synchronous.
const func = async() => {
let new_data = 0
let updated_data = 0
for(value of results.data){
try{
let condition = { VIN: value.VIN }
let doc = await Vehicle.model.findOne(condition);
if (doc) {
let condition2 = { VIN: doc.VIN }
let doc1 = await Vehicle.model.update(condition2)
updated_data += 1
if (doc && typeof doc.log === 'function') {
const data = {
action: 'Update',
category: 'Inventory Import',
message: 'Import Successful',
status: '200'
}
doc.log(data);
}
} else {
let doc2 = await Vehicle.model.create(value);
new_data += 1
if (doc2 && typeof doc2.log === 'function') {
const data = {
action: 'Create',
category: 'Inventory Import',
message: 'Import Successful',
status: '200'
}
doc2.log(data);
}
}
}catch(err){
console.log(err);
}
}
console.log("new datad : ", new_data)
console.log("updated_data : ", updated_data)
}
func();

Related

Exception from a finished function: Error: 10 ABORTED: The referenced transaction has expired or is no longer valid

I'm trying to use transaction function to create/update documents in Firestore Database, but when the function is executed it will abort with error code above.
Here is my code, I cannot figure out what causes this.
By loggers I put in the code, they show only to this line functions.logger.log("target: ", target);
Thank you
.document("chatrooms/{chatRoomId}/message/{messageId}")
.onCreate((snap, context) => {
db.doc(`chatrooms/${context.params.chatRoomId}`)
.get()
.then((parentData) => {
functions.logger.log("data: ", parentData.data());
if (parentData.data().member.length > 0) {
db.runTransaction((transaction) => {
parentData.data().member.map((target) => {
functions.logger.log("target: ", target);
const TrRef = db.doc(
`userDetail/${target}/chatmessage/${context.params.chatRoomId}`
);
return transaction.get(TrRef).then((trDoc) => {
if (!trDoc.empty) {
functions.logger.log("have record");
let readed = false;
if (snap.data().senderId == target) {
readed = true;
}
transaction.update(
db.doc(
`userDetail/${target}/chatmessage/${context.params.chatRoomId}`
),
{
lastmsg: snap.data().text,
sender: snap.data().sender,
senderId: snap.data().senderId,
timestamp: snap.data().timestamp,
readed: readed,
}
);
} else {
functions.logger.log("not have record");
let readed = false;
if (snap.data().senderId == target) {
readed = true;
}
if (
parentData.data().type == "private" ||
parentData.data().type == "chara"
) {
transaction.set(
db.doc(
`userDetail/${target}/chatmessage/${context.params.chatRoomId}`
),
{
lastmsg: snap.data().text,
sender: snap.data().sender,
senderId: snap.data().senderId,
timestamp: snap.data().timestamp,
readed: readed,
type: parentData.data().type,
}
);
} else {
transaction.set(
db.doc(
`userDetail/${target}/chatmessage/${context.params.chatRoomId}`
),
{
thumbnail: parentData.data().thumbnail,
name: parentData.data().name,
lastmsg: snap.data().text,
sender: snap.data().sender,
senderId: snap.data().senderId,
timestamp: snap.data().timestamp,
readed: readed,
type: parentData.data().type,
}
);
}
}
});
});
return Promise.resolve();
}).then();
}
});
});```

why does my cloud function keep giving an error (CloudFirestore with ForOf Loop)?

My function getDocuments() in summary consists in that I pass some parameters in an array (like the path, the name of the document, if I want to section it by parts) and based on that array I return the content of each document through a loop (ForOf), the function I do it more than anything to save me too many lines of code, the problem is that it always throws me an error that I do not know what it is.
Can you help me? Please
Cloud function
export const employees = functions.https.onRequest((request, response) => {
corsHandler(request, response, async () => {
return await security.securityLayer(
{ _definedMethod: "GET", userValue: request.method },
{ _definedType: true, _definedLevel: [4], _definedSeconds: 12, userToken: request.header("_token") },
{ required: false },
{ required: false }
).then(async (answer) => {
if (answer.status === 400 || answer.status === 401) {
return response.status(answer.status).send(answer);
}
return await security.getDocuments([
{ collection: "Core/", documentName: "Centers", options: { idReturn: "centros", nestedProperties: [] } },
{
collection: "Core/", documentName: "Employees", options: {
idReturn: "personal",
nestedProperties: [
{ idReturn: "employees", name: "employee" },
{ idReturn: "submanager", name: "submanager" },
{ idReturn: "manager", name: "manager" }
],
},
},
], SPECIAL_CODE).then((documents) => response.status(documents.status).send(documents))
.catch(() => response.status(500).send(security.error500(SPECIAL_CODE, 2)));
}).catch(() => response.status(500).send(security.error500("SPECIAL_CODE", 1)));
});
});
async function
export async function getDocuments(
documents: {
collection: string,
documentName: string,
options: {
idReturn: string,
nestedProperties: {
idReturn: string,
name: string
}[]
}
}[],
code: string):
Promise<{ status: 201, code: string, subcode: number, devDescription: string, data: any }> {
const data: any = {};
const response: { devDescription: string, subcode: number } = { devDescription: "The document was found and retrieved successfully.", subcode: 1 };
if (documents.length > 1) {
response.devDescription = "Documents were found and obtained successfully.";
response.subcode = 2;
}
for (const iterator of documents) {
const docRef = { path: iterator.collection, name: iterator.documentName };
const options = { id: iterator.options.idReturn, nestedProperties: iterator.options.nestedProperties };
const doc = await database.collection(docRef.path).doc(docRef.name).get();
if (!doc.exists) {
data[options.id] = "The document " + docRef.name + " does not exist in the specified path: " + docRef.path;
if (documents.length === 1) {
response.devDescription = "The document was not found. Check the DATA for more information.";
response.subcode = 3;
} else {
response.devDescription = "One, several or all documents were not found. Check the DATA for more information.";
response.subcode = 3;
}
} else {
const docData: any = doc.data();
if (options.nestedProperties.length === 0) {
data[options.id] = docData;
} else {
for (const nested of options.nestedProperties) {
data[options.id][nested.idReturn] = _.get(docData, nested.name);
}
}
}
}
return { status: 201, code: code, subcode: response.subcode, devDescription: response.devDescription, data: data };
}
I was investigating and I saw that what was causing the error was obviously the loop (ForOf), to solve it I used the Promise.all() method, so the actual code that works for me is the following
export async function getDocuments(
documents: {
collection: string,
documentName: string,
path?: string,
options: {
idReturn: string,
nestedProperties: {
idReturn: string,
name: string
}[]
}
}[],
code: string):
Promise<{ status: number, code: string, subcode: number, devDescription: string, data: any }> {
const idPrimary: any = Object.values(
documents.reduce((c: any, v: any) => {
const k = v.options.idReturn;
c[k] = c[k] || [];
c[k].push(v);
return c;
}, {})
).reduce((c: any, v: any) => (v.length > 1 ? c.concat(v) : c), []);
if (idPrimary.length > 0) {
return {
status: 400, code: code, subcode: 0, data: idPrimary,
devDescription: "Some return IDs are repeated, check your code and replace the return IDs with unique IDs, for more information see the DATA section." };
}
const response: { devDescription: string, subcode: number } = { devDescription: "The document was found and retrieved successfully.", subcode: 1 };
const queries = [];
if (documents.length > 1) {
response.devDescription = "Documents were found and obtained successfully.";
response.subcode = 2;
}
documents.map((document) => {
if (document.path === undefined) {
document.path = document.collection + "/" + document.documentName;
}
});
for (const iterator of documents) {
queries.push(database.collection(iterator.collection).doc(iterator.documentName).get());
}
return Promise.all(queries).then((snapShot) => {
const data: any = {};
snapShot.forEach((doc) => {
const docProperties = documents.find((item) => item.path === doc.ref.path) ?? null;
if (!doc.exists) {
if (docProperties !== null) {
data[docProperties.options.idReturn] = "The document " + doc.id + " does not exist in the specified path: " + doc.ref.path;
if (documents.length === 1) {
response.devDescription = "The document was not found. Check the DATA for more information.";
response.subcode = 3;
} else {
response.devDescription = "One, several or all documents were not found. Check the DATA for more information.";
response.subcode = 3;
}
}
} else {
if (docProperties !== null) {
const docData: any = doc.data();
if (docProperties.options.nestedProperties.length === 0) {
data[docProperties.options.idReturn] = docData;
} else {
data[docProperties.options.idReturn] = {};
for (const nested of docProperties.options.nestedProperties) {
if (nested.name === undefined) {
data[docProperties.options.idReturn][nested.idReturn] = _.get(docData, nested.idReturn);
} else {
data[docProperties.options.idReturn][nested.idReturn] = _.get(docData, nested.name);
}
}
}
}
}
});
return { status: 201, code: code, subcode: response.subcode, devDescription: response.devDescription, data: data };
});
}

How to delete a voice channel when everybody disconnects?

I created a join to create system that creates a channel when a user join and delete it when they leave. However, it only deletes if the last person that's leaving is the user who created the room. Any ideas?
const { Collection } = require("discord.js");
const voiceCollection = new Collection();
module.exports = async (Discord, client, oldState, newState) => {
const user = await client.users.fetch(newState.id);
const member = newState.guild.member(user);
// JOIN
if (!voiceName || voiceName === "") {
if (!oldState.channel && newState.channelID === "898245212541976667") {
const channel = await newState.guild.channels.create(user.tag, {
type: "voice",
parent: newState.channel.parent,
});
member.voice.setChannel(channel);
voiceCollection.set(user.id, channel.id);
await channel.overwritePermissions([
{
id: user.id,
allow: ["MANAGE_CHANNELS", "CONNECT"],
},
{
id: member.guild.id,
deny: ["CONNECT"],
},
]);
} else if (!newState.channel) {
if (oldState.channelID === voiceCollection.get(newState.id)) {
if (oldState.channel.members.size < 1) {
return oldState.channel.delete();
}
}
}
var newchannel_id = config.Channel_id;
var category_id = config.category;
var userchannellist = []
client.login(token);
client.on('voiceStateUpdate', async (oldMember, newMember) => {
if (newMember.channel !== null && oldMember.channel === null && newMember.channel.id === newchannel_id || newMember.channel !== null && oldMember.channel !== null && newMember.channel.id === newchannel_id) {
var current_user = newMember.member.user;
console.log(current_user.username + 'creating the channel');
// Start the creation of the new channel
var server = newMember.guild;
let USERA = newMember.member.nickname || newMember.member.user.username;
var channel = {
type: 'voice', bitrate: 384000, parent: category_id, permissionOverwrites: [{
// permissions
id: server.id, allow: ['VIEW_CHANNEL'],
},
{
id: current_user.id, allow: ['MOVE_MEMBERS', 'MANAGE_CHANNELS']
}
]
};
server.channels.create('🔊' + USERA, channel).then(channel => {
newMember.setChannel(channel).catch(console.error)
userchannellist.push(channel)
//channel region
client.api.channels(channel.id).patch({ data: { rtc_region: "rotterdam" } })
}).catch(console.error);
}
// delete Chaneel
if (oldMember.channel) {
let filter = (ch) =>
(ch.parentID == category_id)
&& (ch.id !== newchannel_id)
&& (oldMember.channel == ch.id)
&& (oldMember.channel.members.size == 0);
return oldMember.guild.channels.cache
.filter(filter)
.forEach((ch) => ch.delete()
.catch(console.error));
}
});

Node js, function doesn't wait for response

I'm new to node js.
Logically, await should wait for the function to complete and then next line of code should run, but in my function, it doesn't wait for process (2) to finish even after using await. What am I missing?
const getDefaultTemplates = async (token) => {
const emailertoken = process.env.EMAILER_AUTH_TOKEN;
const key = 'defaultTemplates';
// This should run first
console.log('hi');
let defaultTemplatesVar = '';
let fromCache = 0;
// This should run second. Need response from redisClient to return parent function
const defaultTemplates = await redisClient.get(key, (err, data) => {
if (err) {
console.log(err);
}
// If data is found in cache
if (data != null) {
console.log('from cache');
defaultTemplatesVar = JSON.parse(data);
fromCache = 1;
console.log('defaultTemplatesVar1 = ', defaultTemplatesVar);
}
return defaultTemplatesVar;
});
console.log('defaultTemplatesVar2 = ', defaultTemplatesVar);
console.log('fromCache = ', fromCache);
if (fromCache === 0) {
// If data is not found in cache, call api
// console.log('from api');
try {
const response = await axios.get(`${process.env.EMAILER_API_URL}/get-system-templates`, {
headers: {
Authorization: `bearer ${emailertoken}`,
},
});
console.log('from data');
redisClient.setex(key, 3600, JSON.stringify(response.data._embedded.get_system_templates));
defaultTemplatesVar = response.data._embedded.get_system_templates;
console.log('defaultTemplatesVar3 = ', defaultTemplatesVar);
} catch (error) {
console.error(error);
}
}
// This should run at last, to return value to parent function getDefaultTemplates()
console.log('bye');
console.log('defaultTemplatesVar4 = ', defaultTemplatesVar);
return defaultTemplates;
};
OUTPUT ->
hi
defaultTemplatesVar2 =
fromCache = 0
from cache
defaultTemplatesVar1 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]
from data
defaultTemplatesVar3 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]
bye
defaultTemplatesVar4 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]
Output should be in series of 1,2,3,4.
Callback function can not be await,
To turn callback fn to Promise fn, check out https://www.npmjs.com/package/redis#promises .
If you are using other packages, you actually can turn callback to promise by
const redisGet = (key) => new Promise((resolve, reject) => {
redisClient.get(key, (err, data) => {
if (err) return reject(err);
return resolve(data);
});
});
// So you can do
const data = await redisGet(key);
// or
redisGet(key).then((data) => { ... }).catch((err) => { ... });
so what you should do
try {
...
const data = await redisClient.get(key);
if (data != null) {
console.log('from cache');
defaultTemplatesVar = JSON.parse(data);
fromCache = 1;
console.log('defaultTemplatesVar1 = ', defaultTemplatesVar);
}
const defaultTemplates = defaultTemplatesVar;
...
} catch (err) {
console.error(err);
}
Simple tutorial about Callback vs Promise
My issue was solved from Allen's Answer.
Only thing I changed in Allen's suggestion was
const data = await redisGet(key);
Instead of
const data = await redisClient.get(key);
because the later one returned 'true' instead of actual values.
const redisGet = (key) => new Promise((resolve, reject) => {
redisClient.get(key, (err, data) => {
if (err) return reject(err);
return resolve(data);
});
});
// Api,Auth Done
const getDefaultTemplates = async (token) => {
const emailertoken = process.env.EMAILER_AUTH_TOKEN;
const key = 'defaultTemplates';
// This should run first
console.log('hi');
let defaultTemplatesVar = '';
let fromCache = 0;
// This should run second. Need response from redisClient to return parent function
try {
const data = await redisGet(key);
if (data != null) {
console.log('from cache');
defaultTemplatesVar = JSON.parse(data);
fromCache = 1;
console.log('defaultTemplatesVar1 = ', defaultTemplatesVar);
}
const defaultTemplates = defaultTemplatesVar;
} catch (err) {
console.error(err);
}
console.log('defaultTemplatesVar2 = ', defaultTemplatesVar);
console.log('fromCache = ', fromCache);
if (fromCache === 0) {
// If data is not found in cache, call api
// console.log('from api');
try {
const response = await axios.get(`${process.env.EMAILER_API_URL}/get-system-templates`, {
headers: {
Authorization: `bearer ${emailertoken}`,
},
});
console.log('from data');
redisClient.setex(key, 3600, JSON.stringify(response.data._embedded.get_system_templates));
defaultTemplatesVar = response.data._embedded.get_system_templates;
console.log('defaultTemplatesVar3 = ', defaultTemplatesVar);
} catch (error) {
console.error(error);
}
}
// This should run at last, to return value to parent function getDefaultTemplates()
console.log('bye');
console.log('defaultTemplatesVar4 = ', defaultTemplatesVar);
return defaultTemplatesVar;
};
OUTPUT 1 (When data comes from the API)
hi
defaultTemplatesVar2 =
fromCache = 0
from data
defaultTemplatesVar3 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]
bye
defaultTemplatesVar4 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]
OUTPUT 2 (When Data comes from Cache)
hi
from cache
defaultTemplatesVar1 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]
defaultTemplatesVar2 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]
fromCache = 1
bye
defaultTemplatesVar4 = [ { name: 'versafix-1', description: 'The versatile template' },
{ name: 'givecentral', description: 'The Givecentral Template' } ]

map() in node.js, async vs sync?

I have a segment code like below running in Node.js. And I find it will always goes to else condiction, howerver with masterData is not null.
getOperationDetails(req, res) {
let sql = 'select a.*, b.s*';
sql += ` from ${paymentSheet} a left join ${paymentHisSheet} b on a.id= b.source_id `;
sql += ' where a.id=? ';
func.connPool(sql, id, (err, rows, field) => {
if (err) {
res.json({ code: 400, message: err })
} else {
let masterData = [];
let details = rows.map((row, idx) => {
if (idx === 0) {
masterData.push({
id: row.id,
name: row.name
});
}
return {
operator: row.operator_info,
comments: row.cmt,
status: row.sta
}
})
if (masterData.length > 0 ) {
masterData[0].details = details;
} else {
console.log(sql);
console.log(id);
console.log('=======================');
console.log(masterData);
}
res.json({ code: 200, message: 'ok', data: masterData })
}
})
For example, the console will show like below. Obviously masterData has value. It means 'if' condiction run before map(). Do I have to use async to wait the map() handle the data over?
allConnections:2
select a.*, b.* from payment a left join history b on a.id= b.source_id where a.id=?
83e588cd-9b4b-4592-ac7f-529bfaa9b231
=======================
allConnections:2
allConnections:2
[
{
id: '83e588cd-9b4b-4592-ac7f-529bfaa9b231',
name: 'Jeff'
}
]
My anaysis:
the rows from database should like below
83e588cd-9b4b-4592-ac7f-529bfaa9b231', 'Jeff', 'Operator Peter', 'OK', 0
83e588cd-9b4b-4592-ac7f-529bfaa9b231', 'Jeff', 'Operator Mary', 'NO', 1
83e588cd-9b4b-4592-ac7f-529bfaa9b231', 'Jeff', 'Operator Jet', 'OK', 2
or like below, means no details
83e588cd-9b4b-4592-ac7f-529bfaa9b231', 'Jeff', null, null, null
That is why I use masterData to separate. I think push() should not be taken out the map(), becasue rows maybe return nothing. Will it be like map() is over and push() is still running?
==== P.S. func.connPool====
let mysql = require('mysql');
let db = require('../configs/db');
let pool = mysql.createPool(db);
module.exports = {
connPool (sql, val, cb) {
pool.getConnection((err, conn) => {
if (err) {
console.log('Connection Error:' + err);
cb(err, null, null);
} else {
console.log('allConnections:' + pool._allConnections.length);
let q = conn.query(sql, val, (err, rows,fields) => {
pool.releaseConnection(conn);
if (err) {
console.log('Query:' + sql + ' error:' + err);
}
cb(err, rows, fields);
});
}
});
},
What I suspected is that the push operation is somehow delay because of some code that is not shown here (I am not certain yet).
I ran the following code so many times, I still could not reproduce your problem.
var rows = [
{
id: "123",
name: "test",
},
{
id: "123",
name: "test",
},
{
id: "123",
name: "test",
},
]
let masterData = [];
let details = rows.map((row, idx) => {
if (idx === 0) {
masterData.push({
id: row.id,
name: row.name
});
}
return {
id: row.id,
name: row.name,
}
})
if (masterData.length > 0 ) {
console.log("in");
} else {
console.log(masterData);
console.log('=======================');
}
Could you try whether it goes to else or not for this code.
From this piece of code you are pushing to MasterData only the first row.id and row.name.
( that is specified in the if conditional for just the first index idx === 0 )
So if thats the case you don't need to have this push thing inside the map.
You can take that out of the map and leave the iterator to create only the details array.
You can go with:
let details = rows.map(row => ({
operator: row.operator_info,
comments: row.cmt,
status: row.sta
})
);
let masterData = [{ id: rows[0].id, name: rows[0].name, details }]

Categories

Resources