await not working for insert command in nodejs - node.js

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;
}
}))

Related

Async function returns pending promise node js

This is a very easy question but no google search results return the correct answer.
const express = require("express");
const app = express();
const cors = require("cors");
const pool = require("./db");
const poolec2 = require("./db");
require("./function")();
returnMappings = async function(connection){
try {
let mapping = await connection.query("SELECT ticker FROM mappings");
let results = await mapping.rows;
//console.log(results);
return results;
} catch (err) {
console.error(err.message);
}
};
const mappings = returnMappings(poolec2);
console.log(mappings);
What am I missing here that is not returning my data? When I unquote console.log(results); I can see the desired results in my terminal. I've tried various versions of using .then but have not had any success return results.then;, const mappings = returnMappings(poolec2).then;, console.log(mappings.then);. I've also tried returning my results outside of my try catch with no luck. I'm really stuck trying to understand how I go about returning a from an async function.
EDIT
The goal is to pass the results from the above async function to another function to check if the a user inputted value exists in that vector of mappings. So indexOf based on a user input, I then use if else to return true or false. With the final results being either true or false.
checkMappings = function(string,input){
stringArray = string;
value = stringArray.indexOf(input);
if(value > -1){
return false
}else{
return true
}
};
SOLUTION
returnMappings = async function(connection,input){
try {
const mapping = await connection.query("SELECT ticker FROM mappings_production");
const results = await mapping.rows;
//console.log(results);
return results;
} catch (err) {
console.error(err.message);
}
};
checkMappings = function(string,input){
let stringArray = JSON.stringify(string);
let value = stringArrayC1.indexOf(input);
function test(a) {
let check;
if(a > -1) {
return true
}else {
return false
}
};
console.log(test(value));
return test(value);
};
const resMappingCheck = returnMappings(poolec2).then((mappings) => checkMappings(mappings,"AAPL"));
console.log(resMappingCheck);
this worked for what I needed to do
As others have pointed out, await can only be used in an async function, but using .then() is functionally equivalent.
This syntax that should work for you:
returnMappings(poolec2).then((mappings) => console.log(mappings));
if you want to do something more elaborate / multi-line, you can use curly braces like so:
returnMappings(poolec2).then((mappings) => {
console.log(mappings)
});
UPDATE:
If you want to chain two functions together, then you'll need to start with the .then() pattern: you can declare the callback function in .then() to be asynchronous. At that point, you can start to use await like you're used to.
I'm not sure what relationship you're trying to create between returnMappings() and checkMappings(), but you can chain them together like this: (note the use of async on the first line to allow the use of await inside the callback.)
returnMappings('test').then(async (mapping) => {
const checkResult = await checkMappings(mapping)
console.log(`checkMapping result: ${checkResult}`)
}).catch((err) => console.log(`Error: ${err}`))
Try this:
const mappings = await returnMappings(poolec2);
That will work if you wrap the code inside an async function. Or you could do:
let mappings;
returnMappings(poolec2).then(res => {
mappings = res;
});

Synchronously iterate through firestore collection

I have a firebase callable function that does some batch processing on documents in a collection.
The steps are
Copy document to a separate collection, archive it
Run http request to third party service based on data in document
If 2 was successful, delete document
I'm having trouble with forcing the code to run synchronously. I can't figure out the correct await syntax.
async function archiveOrders (myCollection: string) {
//get documents in array for iterating
const currentOrders = [];
console.log('getting current orders');
await db.collection(myCollection).get().then(querySnapshot => {
querySnapshot.forEach(doc => {
currentOrders.push(doc.data());
});
});
console.log(currentOrders);
//copy Orders
currentOrders.forEach (async (doc) => {
if (something about doc data is true ) {
let id = "";
id = doc.id.toString();
await db.collection(myCollection).doc(id).set(doc);
console.log('this was copied: ' + id, doc);
}
});
}
To solve the problem I made a separate function call which returns a promise that I can await for.
I also leveraged the QuerySnapshot which returns an array of all the documents in this QuerySnapshot. See here for usage.
// from inside cloud function
// using firebase node.js admin sdk
const current_orders = await db.collection("currentOrders").get();
for (let index = 0; index < current_orders.docs.length; index++) {
const order = current_orders.docs[index];
await archive(order);
}
async function archive(doc) {
let docData = await doc.data();
if (conditional logic....) {
try {
// await make third party api request
await db.collection("currentOrders").doc(id).delete();
}
catch (err) {
console.log(err)
}
} //end if
} //end archive
Now i'm not familiar with firebase so you will have to tell me if there is something wrong with how i access the data.
You can use await Promise.all() to wait for all promises to resolve before you continue the execution of the function, Promise.all() will fire all requests simultaneously and will not wait for one to finish before firing the next one.
Also although the syntax of async/await looks synchronous, things still happen asynchronously
async function archiveOrders(myCollection: string) {
console.log('getting current orders')
const querySnapshot = await db.collection(myCollection).get()
const currentOrders = querySnapshot.docs.map(doc => doc.data())
console.log(currentOrders)
await Promise.all(currentOrders.map((doc) => {
if (something something) {
return db.collection(myCollection).doc(doc.id.toString()).set(doc)
}
}))
console.log('copied orders')
}

NodeJS execute the next process after then() [duplicate]

This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 3 years ago.
I'm trying to do upsert inside forEach becase the request is an array of objects using Sequelize.upsert method. I do this:
async createInfo (req, res){
newsData = req.body.news,
newsList = [];
newsData.forEach(async values => {
var news = {};
news.inserted_id = values.inserted_id;
if(news.inserted_id == null){
do {
news.inserted_id = crypto.getRandom(5);
var check = await InstitutionNews.count({where: {inserted_id: news.inserted_id}});
} while (check > 0);
}
News.upsert({
institution_id: institution_id,
inserted_id: news.inserted_id,
news_date: values.news_date,
news_title: values.news_title,
description: values.news_description,
created_by: created_by
}).then(ResNews => {
news.news_date = values.news_date;
news.news_title = values.news_title;
news.description = values.news_description;
newsList.push(news);
})
})
console.log("TEST")
}
but the process stop at the then(). It didn't execute the next code like the console.log.
Is there any way to execute next line code after the then(). I need then() because I wanna push the news object into newsList array. Because I need newsList as the if else conditional to do the next process.
Thanks.
Since it sounds like you need to wait for forEach to complete before you do another step, so I'd suggest using something like Promise.all:
async function createInfo(req, res) {
const newsData = req.body.news
const newsList = []
try {
await Promise.all(
newsData.map(
async (values) => {
const news = {}
news.inserted_id = values.inserted_id
// ...
News.upsert({...})
.then(ResNews => {
// ...
newsList.push(news)
})
}
)
)
console.log('newsList', newList)
// do other work
} catch (error) {
// handle errors appropriately
}
}
This way, you're creating an array of promises and waiting for all of them to resolve/finish.
Your current approach with forEach won't work in this case since it won't wait for each asynchronous call to finish before executing the "next" step. Since Promise.all returns a single Promise that you can then "wait" for to resolve before continuing with your next step.
Here's a simple example that somewhat does what you're trying to do:
async function createInfo() {
const newsData = [1, 2, 3, 4, 5]
const newsList = []
await Promise.all(
newsData.map(
async (values) => {
const temp = await Promise.resolve('temp')
console.log('first async call inside values of', values)
Promise.resolve('resolved')
.then((result) => {
newsList.push(`resolved with ${values}`)
})
}
)
)
console.log('newsList after')
console.log(newsList)
}
createInfo()
EDIT
Here's an alternative solution as rightly pointed by #Bergi in the comments:
async function createInfo(req, res) {
const newsData = req.body.news
try {
const newList = await Promise.all(
newsData.map(
async (values) => {
const news = {}
news.inserted_id = values.inserted_id
// ...
// since it doesn't look like you're using any
// data that you'd get back from the `News.upsert` call, wait for
// it to finish and just simply return your `news` object
await News.upsert({...})
news.news_date = values.news_date
news.news_title = values.news_title
news.description = values.news_description
return news
}
)
)
console.log('newsList', newList)
// do other work
} catch (error) {
// handle errors appropriately
}
}
use await instead of then like
await News.upsert({
institution_id: institution_id,
inserted_id: news.inserted_id,
news_date: values.news_date,
news_title: values.news_title,
description: values.news_description,
created_by: created_by
});
news.news_date = values.news_date;
news.news_title = values.news_title;
news.description = values.news_description;
newsList.push(news);

this.parent.acquire is not a function on prepared statements

I use the mssql (https://www.npmjs.com/package/mssql) module for my database. Normally I use postgres databases which lead to pg (https://www.npmjs.com/package/pg).
I want to setup prepared statements for the mssql database. When using the pg module it's quite easy.
This is how I do it with pg:
I setup my databaseManager
const { Pool } = require('pg');
const db = require('../config/database.js');
const pool = new Pool(db);
function queryResponse(result, err) {
return {
result,
err
};
}
module.exports = async (text, values) => {
try {
const result = await pool.query(text, values);
return queryResponse(result.rows, null);
} catch (err) {
return queryResponse(null, err);
}
};
and whenever I want to query the database I can call this module and pass in my statement and values. An example for todo apps would be
todos.js (query file)
const db = require('../databaseManager.js');
module.exports = {
getAllTodosFromUser: values => {
const text = `
SELECT
id,
name,
is_completed AS "isCompleted"
FROM
todo
WHERE
owner_id = $1;
`;
return db(text, values);
}
};
I wanted to create an mssql equivalent. From the docs I see that the module differs from the pg module.
I changed my databaseManager to
const sql = require('mssql');
const config = require('../config/database.js');
const pool = new sql.ConnectionPool(config).connect();
module.exports = async (queryBuilder) => {
try {
const preparedStatement = await new sql.PreparedStatement(pool);
return queryBuilder(sql, preparedStatement, async (query, values) => {
await preparedStatement.prepare(query);
const result = await preparedStatement.execute(values);
await preparedStatement.unprepare();
return {
result: result.rows,
err: null
};
});
} catch (err) {
return {
result: null,
err
}
}
};
and my query file would pass in the required parameters for the preparedStatement object
const db = require('../databaseManager.js');
module.exports = {
getUserByName: username => db((dataTypes, statementConfigurator, processor) => {
statementConfigurator.input('username', dataTypes.VarChar);
const query = `
SELECT
*
FROM
person
WHERE
username = #username;
`;
return processor(query, { username });
})
};
I was hoping that this approach would return the desired result but I get the error
this.parent.acquire is not a function
and don't know if my code is wrong. If it is, how can I setup my prepared statements correctly?
Edit:
I just found out that the error comes from this line of code
await preparedStatement.prepare(query);
but I think I took it correctly from the docs
https://tediousjs.github.io/node-mssql/#prepared-statement
I thought this question deserved a little bit more explanation than the answer what OP gave. The solution is no different than what OP already answered.
The issue remains same, pool mustn't have resolved from its promise pending state. So it just has to be awaited.
module.exports = async queryBuilder => {
try {
await pool; // Waiting for pool resolve from promise pending state.
const preparedStatement = await new sql.PreparedStatement(pool);
// ..
} catch (err) {
// ..
}
};
When you try to build a prepared statement, you pass the pool as an argument to its constructor. In its constructor is the below line
this.parent = parent || globalConnection
After which when you prepare the statement the flow leads to this line which would cause the issue since at that time this.parent's value was still a promise which was yet to be resolved.
this.parent.acquire(this, (err, connection, config) => {

NodeJs async sql to variable

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')
}
};

Resources