Async await not working in forEach - node.js

I have the following code:
async function goodUsers(users) {
const filteredUsers = [];
users.forEach(async (userInstance) => {
console.log('TEST1');
const fromUserContacts = await db.userContactInvite.findAll({
where: {
fromUserId: userInstance.id,
},
});
console.log('TEST2');
await fromUserContacts.forEach((fromUserContact) => {
console.log('TEST3');
const userJson = fromUserContact.toJSON();
userJson.contactState = 'INVITATION_SENT';
filteredUsers.push(userJson);
});
console.log('TEST4');
});
console.log('FILTERED', filteredUsers);
return filteredUsers;
}
When I call goodUsers I get the following output:
TEST1
FILTERED
TEST2
TEST3
TEST4
FILTERED should be last (obviously).
I tried various options but I seem to be not understanding something here. Do you guys notice what's going on?

This's a correct behavior, async/await only affect the function where they are used. So you need to replace forEach which calls callback for each element, to for operator:
async function goodUsers(users) {
const filteredUsers = [];
for(user in users) {
console.log('TEST1');
const fromUserContacts = await new Promise(resolve => setTimeout(() => resolve(['c1', 'c2']), 500));
console.log('TEST2');
fromUserContacts.forEach(fromUserContact => {
console.log('TEST3');
filteredUsers.push('json');
});
console.log('TEST4');
}
console.log('FILTERED', filteredUsers);
return filteredUsers;
}
goodUsers(['u1', 'u2']);

Related

async/await troubles in a recursive Redis function

Ima rookie using async/await but must now to use Redis-om. NN_walkd walks through a Redis database looking for loop-chains and does this by recursion. So the 2 questions I have is:
Am I calling the inner recursive NN_walkd calls correctly via async/await?
At runtime, the compSearchM proc is called first and seems to work (it gets 5 entries so it has to call NN_walkd 5 times). A NN_walkd is then recursively called, and then when it loops the 1st time it then calls compSearchK where the problems are. It seems to sit on the first Redis call in compSearchK (.search). Yet the code for compSearchK and compSearchM look basically identical.
main call
NN_walk = async function(req, db, cnode, pnode, chain, cb) {
var vegas, sneaker;
req.session.walk = [];
await NN_walkd(req, cnode, pnode, [], 1);
req.session.walk = null;
console.log('~~~~~~~~~~~~ Out of Walk ~~~~~~~~~~~~~~~');
cb();
};
redis.mjs
export class RedisDB {
constructor() {
...
this._companyRepo = ...
}
compSearchK(ckey) { // doesn't matter if I have a async or not here
return new Promise(async (resolve) => {
const sckey = await this._companyRepo.search()
.where('COMPANYKEY').equals(ckey)
.return.all();
if (sckey.length) {
const ttrr = await this._companyRepo.fetch(sckey[0].entityId);
resolve(ttrr.toJSON());
} else
resolve(null);
});
}
compSearchM(mkey) {
var tArr=[];
return new Promise(async (resolve) => {
const smkey = await this._companyRepo.search()
.where('MASTERKEY').equals(mkey)
.and('TBLNUM').equals(10)
.return.all();
if (smkey.length) {
for (var spot in smkey) {
const ttrr = await this._companyRepo.fetch(smkey[spot].entityId);
tArr.push(ttrr.toJSON());
}
resolve(tArr);
} else {
resolve(null);
}
});
}
walk.js
NN_walkd = async function(req, cnode, pnode, chain, lvl) {
...
if (cnode[1]) {
const sObj = await req.app.get('redis').compSearchK(cnode[1]);
if (sObj) {
int1 = (sObj.TBLNUM==1) ? null : sObj.CLIENTKEY;
(async () => await NN_walkd(req, [sObj.COMPANYKEY,int1], cnode, Array.from(chain), tlvl))()
}
} else {
const sArr = await req.app.get('redis').compSearchM(cnode[0]);
if (sArr.length) {
for (sneaker in sArr) {
(async () => await NN_walkd(req, [sArr[sneaker].COMPANYKEY,sArr[sneaker].CLIENTKEY], cnode, Array.from(chain), tlvl))()
}
} else {
console.log('no more links on this chain: ',cnode);
}
}
}
"doesn't matter if i have async or not here"
compSearchK(ckey) { // doesn't matter if I have a async or not here
return new Promise(async (resolve) => {
const sckey = await this._companyRepo.search()
.where('COMPANYKEY').equals(ckey)
.return.all();
if (sckey.length) {
const ttrr = await this._companyRepo.fetch(sckey[0].entityId);
resolve(ttrr.toJSON());
} else
resolve(null);
});
}
Of course it doesn't matter, because you're not using await inside of compSearchK!
You are using the explicit promise contructor anti-pattern. You should avoid it as it demonstrates lack of understanding. Here is compSearchK rewritten without the anti-pattern -
async compSearchK(ckey) {
// await key
const sckey =
await this._companyRepo.search()
.where('COMPANYKEY').equals(ckey)
.return.all();
// return null if key is not found
if (sckey.length == 0) return null;
// otherwise get ttrr
const ttrr = await this._companyRepo.fetch(sckey[0].entityId);
// return ttrr as json
return ttrr.toJSON();
}

Jest Testing - mockReset does not clear the already set values

I have a lambda which calls an interface. Am trying to mock the functions imported from the interface. In each test case, I have to mock different values. When I run all the test cases, the first mocked value does not get cleared and it is carried over to all Test cases
interface.js
const oracledb=require("oracledb");
module.exports = class dbLayer{
execSql= async (sqlQuery) => {
var result;
var binds={};
result = await oracledb.execute(sqlQuery,binds);
return result
}
}
lambda.js
const qryInterface = require(./interface)
var db;
const handler = async (event, context) => {
var sql= event.query
db = new qryInterface();
var dbResponse = await db.execSql(sql)
return dbResponse
}
test.js
const index = require(./lambda)
const dbMock = require(./interface)
jest.mock(./interface);
var mockResponse1 = {
rows: [
{
col1 : 123
}
]
}
var mockResponse2 = {
rows: [
{
col1 : 456
}
]
}
const firstQryResponse = async () => {
return new Promise((resolve, reject) => {
resolve(mockResponse1);
});
};
const secondQryResponse = async () => {
return new Promise((resolve, reject) => {
resolve(mockResponse2);
});
};
describe("1st test suite", () => {
beforeEach(() => {
dbMock.mockReset();
jest.restoreAllMocks();
});
it("Test 1", asycn () => {
var event={}
event.sql=`SELECT col1 FROM TBL_A WHERE col2 = 'A'`
dbMock.mockImplementation(() => {
return {
execSql:firstQryResponse
}
});
var response = await index.handler(event)
expect(response).toEqual(mockResponse1 )
});
it("Test 2", asycn () => {
var event={}
event.sql=`SELECT col1 FROM TBL_A WHERE col2 = 'B'`
dbMock.mockImplementation(() => {
return {
execSql:secondQryResponse
}
});
var response = await index.handler(event)
expect(response).toEqual(mockResponse2 )
});
});
Test 1 mocks the value set in firstQryResponse and Test 2 mocks secondQryResponse. But what happens is the value mocked for execSql is Not getting reset in Test 2 .i.e. firstQryResponse gets carried over to Test 2.
Because of this, 2nd test fails. If i run them individually, it runs fine. I did try mockReset(), but it does not help as well.
Appreciate any help on this
Thanks
Sadiq

how to refer to return value of a function from outside in Node.js

I've been making dynamic dropdown box that each option has the table's name of BigQuery and I want to use return value (list) that is made inside .then method in function listTables(). However it seems not to work well . I'm new to Js so could you give any tips ?? Thank you so much.
function listTables() {
const {BigQuery} = require('#google-cloud/bigquery');
const bigquery = new BigQuery({
projectId: 'test_project',
});
const list = [];
bigquery
.dataset("test_table")
.getTables()
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
return console.log(list);←I want to use this list outside a function
})
.catch(err => {
console.error('ERROR:', err);
});
}
listTables();
// select tag
let slt = document.getElementById("slt");
addTables(slt);
// return data
function getList() {
return new Promise(function (onFulliflled, onRejected) {
onFulliflled(list);
});
}
function addTables(slt) {
getList()
.then((list) => {
for (item of list) {
// optionを作成
let option = document.createElement("option");
option.text = item;
option.value = item;
// optionの追加
slt.appendChild(option);
}
})
.catch((err) => {
console.error("error", err);
});
}
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
return console.log(list);
})
RESULT
[ 'test', 'test1', 'test2', 'test3' ]
You can do this in multiple ways.
Using calllback
function listTables(callback) {
//...
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
callback(list);
})
//...
}
listTables(function(list){
});
Using promise or async/await
function listTables() {
return new Promise(function(resolve, reject){
//...
.then(results => {
const tables = results[0];
tables.forEach(table => list.push(table.id));
resolve(list);
})
//...
});
}
// Promise
listTables().then(function(list){
});
//Async/await
var list = await listTables();
For the await to work you also need to run in within an async function. For example wrap it in async iife.
(async function(){
var list = await listTables();
})();
I don't use await myself so this is just from top of my head and might need some changes.

Strange Promise.all behaviour

In one file called SearchBuilder.js i have this function ( wrote in this way as pure test )
self.validateInputs = async params => {
const validateDates = async () => {
const pick_up_date = moment(`${params.pick_up_date} ${params.pick_up_time}`),
drop_off_date = moment(`${params.drop_off_date} ${params.drop_off_date}`);
return true;
};
const validatePickUpId = async () => {
return true;
};
const validateDropOffId = async () => {
throw { 'code': 'NOT_FOUND'};
};
return Promise.all([
validateDates,
validatePickUpId,
validateDropOffId,
]);
};
In another file called searchEngine.js i have this:
self.run = async (type, search_inputs, account) => {
const searchBuilder = self.getSearchBuilder(type);
try {
const isValid = await searchBuilder.validateInputs(search_inputs);
console.log(isValid);
} catch (e) {
console.log('error input validation');
}
return search;
};
I erroneously thought that if any of the promises in Promise.all would fail, so my code will enter the catch block. Instead, even if the promise fails code inside the catch is never executed and inside isValid i get this.
[ [AsyncFunction: validateDates],
[AsyncFunction: validatePickUpId],
[AsyncFunction: validateDropOffId] ]

Why isn't my Async Await flow not showing the results expected?

Im using Async on getJsonProducts, and I'm awaiting before console.log(products) but it still prints undefined. I thought await, pauses until the promise has been resolved?
Why aren't the products visible?
async function getJsonProducts(){
let products;
await fs.readFile('./products.json','utf-8',async (err,data)=>{
if(err)
throw err;
let r = /\"\_id.*\,(?=\"info\")|(?<=hex.*\})\,\"\_+v.*\d/g
let d = await data.replace(r,'');
d = await d.split('\n');d.pop();
products = await d.map(s=>JSON.parse(s));
//console.log(products) prints here
})
await console.log(products); //prints undefined here?
}
const seedProducts = async () => {
await getJsonProducts();
}
seedProducts();
I know there's other ways to implement this but I want to understand why this isn't working.
absolutely you will get undefined because you combined between async-await and the callback, also this is not how async-await works,
if you want to use async await , you can follow my code
async function getJsonProducts() {
return new Promise((reoslve, reject) => {
fs.readFile('./products.json', 'utf-8', async (err, data) => {
if (err) reject(err)
let r = /\"\_id.*\,(?=\"info\")|(?<=hex.*\})\,\"\_+v.*\d/g
let d = data.replace(r, '');
d = d.split('\n');
d.pop();
const products = d.map(s => JSON.parse(s));
resolve(products)
})
})
}
const seedProducts = async () => {
const products = await getJsonProducts();
conosle.log(products); // you will get products
}
seedProducts();
function getFile(cb) {
fs.readFile('./products.json', 'utf-8', (err, data) => {
if (err)
throw err;
let r = /\"\_id.*\,(?=\"info\")|(?<=hex.*\})\,\"\_+v.*\d/g
let d = data.replace(r, '');
d = d.split('\n');
d.pop();
cb(d.map(s => JSON.parse(s)));
})
}
async function getJsonProducts() {
this.getFile(products => console.log(products));
}
const seedProducts = async () => {
await getJsonProducts();
}
seedProducts();

Resources