How to make sequential if statements asynchronous in JavaScript? - javascript

I have a series of if statements in a loop like this:
for( var i = 0; i < results_list.length; i++){
find = await results_list[i];
//result 1
if (find.Process == "one") {
await stored_proc(38, find.Num, find.Status)
}
//result 2
if(find.Process == "two") {
await stored_proc(37, find.Num, find.Status)
}
//result 3
if(find.Process == "three") {
await stored_proc(39, find.Num, find.Status)
}
}
My issue is that it runs all of these synchronously causing my stored procedure to trip over itself. How can I ensure each if statement waits for the previous one to complete before running?
It is also important to know that each if statement is not always run, for instance on one run of my code //result 2 may run and //result 1 and //result 3 may not run.
Sometimes they might all run and sometimes none will run at all.
Thanks for any help!
EDIT: Here is my stored procedure function
async function stored_proc(opID, num, stat){
sql.executeTransaction( connection, {
procedure: "<stored procedure>",
params: {
OpID: {
val: opID,
type: sql.INT
},
num: {
val: num,
type: sql.STRING
},
Pass: {
val: stat,
type: sql.INT
},
ExtraData: {
val: "upload",
type: sql.STRING
}
}
} ).then( async function( data ) {
return data.transaction
.commit()
.then( async function() {
console.log("Updated database...." );
} );
}, function( err ) {
console.log( err );
} );
}
SECOND EDIT: I have looked into this some more and found that the if there is more than one result to upload it will NEVER upload the first sets of results. I have ran some console.log()s through and found it will always get find.Num and find.Status. It will only log Updated database for every result after the first one. I hope this makes sense

In your stored_proc you are not returning the Promise.
Also, promise inside promise is considered anti-pattern you can chain it easily.
async function stored_proc(opID, num, stat) {
return sql.executeTransaction(connection, {
procedure: "<stored procedure>",
params: {
OpID: {
val: opID,
type: sql.INT
},
num: {
val: num,
type: sql.STRING
},
Pass: {
val: stat,
type: sql.INT
},
ExtraData: {
val: "upload",
type: sql.STRING
}
}
})
.then(function (data) {
return data.transaction
.commit()
})
.then(function () {
console.log("Updated database....");
})
.catch((err) => {
console.log(err);
})
}

Related

nodejs filtering an array of objects where the filtering is partially done in an async function

I've read many similar questions and have tried a bunch of code. Unfortunately, I'm not getting my code to run :-(
So, the situation is as follows: In a route of a node.js server, I have to respond with a filtered array of Objects. Unfortunately, whatever I do, I always get an empty array [] back. The filter is a bit tricky in my opinion, as it consists of a string comparison AND an async call to a library function. With the console output, I can clearly see that the correct element is found, but at the same time I see that I've already received the object...
Here is some code that exemplifies my challenge:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
// code from a library. Can't take an influence in it.
async function booleanWhenGood(id) {
if (id in some Object) {
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
// Should return only elements with type 'ofInterest' and that the function booleanWhenGood is true
router.get('/', function(res,req) {
tryOne(testArray).then(tryOneResult =>{
console.log('tryOneResult', tryOneResult);
});
tryTwo(testArray).then(tryTwoResult => {
console.log("tryTwoResult ", tryTwoResult);
});
result = [];
for (const [idx, item] of testArray.entries() ) {
console.log(idx);
if (item.data.someDoc.type === "ofInterest") {
smt.find(item.id).then(element => {
if(element.found) {
result.push(item.id);
console.log("ID is true: ", item.id);
}
});
}
if (idx === testArray.length-1) {
// Always returns []
console.log(result);
res.send(result);
}
}
})
// A helper function I wrote that I use in the things I've tried
async function myComputeBoolean(inputId, inputBoolean) {
let result = await booleanWhenGood(inputId)
if (result.myBoolean) {
console.log("ID is true: ", inputId);
}
return (result.myBoolean && inputBoolean);
}
// A few things I've tried so far:
async function tryOne(myArray) {
let myTmpArray = []
Promise.all(myArray.filter(item => {
console.log("item ", item.id);
myComputeBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
tmpjsdlf.push(item.id);
return true;
}
})
})).then(returnOfPromise => {
// Always returns [];
console.log("returnOfPromise", myTmpArray);
});
// Always returns []
return(myTmpArray);
}
async function tryTwo(myArray) {
let myTmpArray = [];
myArray.forEach(item => {
console.log("item ", item.id);
myCompuBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
myTmpArray.push(item.did);
}
})
});
Promise.all(myTmpArray).then(promiseResult => {
return myTmpArray;
});
}
Asynchronous programming is really tough for me in this situation... Can you help me get it running?
I didn't inspect your attempts that closely, but I believe you are experiencing some race conditions (you print return and print the array before the promises resolve).
However you can alwayd use a regular for loop to filter iterables. Like this:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
async function booleanWhenGood(id) {
if (id in { 'stringId1': 1, 'stringId2': 1 }) { // mock object
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
async function main() {
let filtered = []
for (item of testArray)
if ((await booleanWhenGood(item.id)).myBoolean && item.data.someDoc.type === 'ofInterest')
filtered.push(item)
console.log('filtered :>> ', filtered);
}
main()

How can i can wait to call function before updating the data in Vue js?

I have two API get methods.
The first is to check if the user is an admin.
The second is to call all the user lists.
But I am having a hard time calling first;
It always calls all the user lists before I check if the user is admin or not.
What I did was
async mounted() {
await this.getCurrentUser();
}
and method to check if user is admin:
getCurrentUser(){
Common.getUser().then(data => {
if(!data.admin){
this.isAdmin = false;
}
});
},
method to get all users:
paging: function (pageNumber) {
vm.requestParam.page = pageNumber == undefined ?
var param = Common.param(vm.requestParam);
axios.get('/api/users?' + param).then(function (response) {
///
}
})
.catch(function (error) {
console.log(error.response);
});
},
and the data is:
data: {
resources: {},
'requestParam': {
'id': '',
'query': '',
'accessLevel': '',
'page': 1
},`
The problem is that promise chains are broken, so await has no effect. As a rule of thumb, a function that involves promises should return a promise that represents the end of its job. It should be:
return Common.getCurrentUser()...
and
return axios.get('/api/users?' + param)...
Callback-based nextTick can result in race condition. In case it affects other methods, it should be a promise, too:
await this.$nextTick();
vm.getResources();
...

await still sets variable to undefined and setting returned or callbacked functions to global variables

async function displayEmbed(args, msg) {
var bridgeembed = await getBridgeClutcherStats(args, msg) //should set bridgeembed to an embed
var omniembed = await getOmniClutcherStats(args, msg) //should set omniembed to an embed
var extembed = await getExtClutcherStats(args, msg) //should set extembed to an embed
var desccc = "```Bridge Clutch: 🧱```\n" + "```Omni Clutch: ⚛```\n" + "```Extension Clutch: 📏```\n";
new Menu(msg.channel, msg.author.id, [{
name: "main",
content: new MessageEmbed({
title: "Please Choose a Gamemode",
description: desccc,
url: "https://3d4h.world",
color: 16711935,
author: {
name: args[1],
url: `https://namemc.com/profile/${args[1]}`,
icon_url: `https://mc-heads.net/body/${args[1]}`
}
}),
reactions: {
"🚫": "stop",
"🧱": "bridgeclutch",
"⚛": "omniclutch",
"📏": "extclutch"
}
},
{
name: "bridgeclutch",
content: bridgeembed,
reactions: {
"◀": "main"
},
name: "omniclutch",
content: omniembed,
reactions: {
"◀": "main"
},
name: "extclutch",
content: extembed,
reactions: {
"◀": "main"
}
}
]);
}
So I've been trying to have three functions run before I create an embed menu. I followed these (How to synchronously call a set of functions in javascript) steps but bridgeembed, omniembed, and extembed still ended up as undefined.
When I directly sent the embed from the functions, it worked. I also tried using callbacks like this:
getOmniClutcherStats(args, msg, function(omniclutchEmbed){
getExtClutcherStats(args, msg, function(extclutchEmbed){
getBridgeClutcherStats(args, msg, function(bridgeclutchEmbed) {
new Menu(msg.channel, msg.author.id, [{
name: "main",
content: new MessageEmbed({
title: "Please Choose a Gamemode",
description: desccc,
url: "https://3d4h.world",
color: 16711935,
author: {
name: args[1],
url: `https://namemc.com/profile/${args[1]}`,
icon_url: `https://mc-heads.net/body/${args[1]}`
}
}),
reactions: {
"🚫": "stop",
"🧱": "bridgeclutch",
"⚛": "omniclutch",
"📏": "extclutch"
}
},
{
name: "bridgeclutch",
content: bridgeembed,
reactions: {
"◀": "main"
},
name: "omniclutch",
content: omniembed,
reactions: {
"◀": "main"
},
name: "extclutch",
content: extembed,
reactions: {
"◀": "main"
}
}
]);
});
});
});
But only when getting bridgeembed it worked, for the others UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'content' of undefined
How can I save the returned or callbacked embeds to variables so I can access them in the reaction menu?
Thanks in advance!
As mentioned by #o-jones and #jfriend00 you'll need to return something directly from your functions in order to have them available for use in displayEmbed. Further, as mentioned by #jfriend00 if you want to use async/await to ensure the calls happen in sequence, you'll need to ensure that the return type of the functions is a Promise that will resolve with the result, or the result itself if that can be determined synchronously.
Looking over the provided code for getExtClutcherStats in that PasteBin, you are calling a few asynchronous functions:
connection.query
MojangAPI.nameToUuid
MojangAPI.profile
average
all using the NodeJS style callback passing pattern. Additionally, in your happy path, you are returning extclutchembed nested inside all of these calls:
connection.query('SELECT * FROM C3', function (error, rows, fields) {
…
MojangAPI.nameToUuid(args[1], function (err, res) {
…
MojangAPI.profile(rows[playerNum].UUID, function (err, res) {
…
average(`/home/bardia/3d4hbot/${res.name}.png`, (err, color) => {
…
return extclutchEmbed
}
…
}
…
}
…
}
Given that structure, extclutchEmbed will only be returned to the immediate callback function (average in the happy path).
In this case, the quickest way to ensure that a Promise is returned would be to wrap the logic inside the function passed to the Promise constructor, and to pass the result you would like returned on promise resolution to the resolve function, like so:
function getExtClutcherStats (args, msg) {
…
return new Promise((resolve, reject) => {
…
connection.query('SELECT * FROM C3', function (error, rows, fields) {
…
MojangAPI.nameToUuid(args[1], function (err, res) {
…
MojangAPI.profile(rows[playerNum].UUID, function (err, res) {
…
average(`/home/bardia/3d4hbot/${res.name}.png`, (err, color) => {
…
resolve(extclutchEmbed)
return
}
…
}
…
}
…
}
});
}
You'll need to work through the additional places that you return in that function to ensure that either the caller can handle them or, if you want to push the error handling onto the caller, you can use the reject function; for example, near the top:
if (args[1].length > 16) {
msg.channel.send('Invalid Player Name')
return
}
could become:
if (args[1].length > 16) {
reject('Invalid Player Name')
return
}
and the call site could then become:
try {
var extembed = await getExtClutcherStats(args, msg)
} catch (error) {
msg.channel.send(error);
}

Postgres "for Each" RangeError: Maximum call stack size exceeded

I'm using sequelize, and I have a function that uses findOrCreate, however it seems to throw and error "RangeError: Maximum call stack size exceeded" despite sometimes adding the entries.
The code I use is below, and I am passing it 6 tids. Does anyone know whats going on and how to resolve/prevent this?
exports.sqlAddTags = async function(did, tids) {
try {
await sequelize.authenticate();
const promises = [];
tids.forEach(t =>{
console.log(did, t)
promises.push(FB_Tag.findOrCreate({
where: {
did: did,
tid: t
}
}));
});
return await Promise.all(promises);
} catch (e) {
console.log(e.message)
}
};
My model:
FB_Tag.init({
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
did: {
type: Sequelize.STRING,
allowNull: false,
},
tid: {
type: Sequelize.STRING,
allowNull: false
},
}, {
sequelize,
modelName: 'fb_tag',
timestamps: false
});
EDIT: Seems with just 1 tid, it also throws this error
If the item already exists, I seem to get this message. Not sure if this is normal?
CREATE OR REPLACE FUNCTION pg_temp.testfunc(OUT response "public"."fb_tags", OUT sequelize_caught_exception text) RETURNS RECORD AS $func_66aef14340b2417dabf18d3cdfafa589$ BEGIN INSERT INTO "public"."fb_tags" ("i
d","did","tid") VALUES (DEFAULT,'jcBUnwocFS9I7RekMZHJ','7AQS1zPkLIUsSfx8lC0a') RETURNING * INTO response; EXCEPTION WHEN unique_violation THEN GET STACKED DIAGNOSTICS sequelize_caught_exception = PG_EXCEPTION_DETAIL; END $func_66aef14340b2417dabf18d3cdfafa589$ LANG
UAGE plpgsql; SELECT (testfunc.response).*, testfunc.sequelize_caught_exception FROM pg_temp.testfunc(); DROP FUNCTION IF EXISTS pg_temp.testfunc();
I've created a test script with the same logic as above that works, so I can only put this down to be how the function is called..
const newTags = [];
const oldTags = [];
const postgresTags = []
data.tags.forEach(t => {
if(t.id === t.title) {
newTags.push(t.title)
} else {
oldTags.push(t.id);
postgresTags.push(t.id)
}
});
if(oldTags.length > 0) {
await firestoreAddTagsToDesign(context.auth.uid, oldTags, data.did, "old");
}
if(newTags.length > 0) {
const setTags = await firestoreAddNewTags(context.auth.uid, newTags, data.did);
await firestoreAddTagsToDesign(context.auth.uid, setTags, data.did, "new");
setTags.forEach(i => postgresTags.push(i.id));
}
await sqlAddTags(data.did, postgresTags);

Jquery Deferred. Multiple ajax calls. deferred vs async false

I've got the following.
var lookupInit = function () {
http.get('api/employmenttype', null, false)
.done(function (response) {
console.log('loaded: employmenttype');
vm.lookups.allEmploymentTypes(response);
});
http.get('api/actionlist', null, false)
.done(function (response) {
console.log('loaded: actionlist');
vm.lookups.allActionListOptions(response);
});
http.get('api/company', null, false)
.done(function (response) {
console.log('loaded: company');
vm.lookups.allCompanies(response);
});
//... x 5 more
return true;
};
// somewhere else
if (lookupInit(id)) {
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
http.json('api/adimport', { by: "latest" }, false).done(viewInit);
}
else if (id !== undefined) {
console.log('api/adimport: transaction');
http.json('api/adimport', { by: "transaction", TransactionId: id }, false).done(viewInit);
}
} else {
console.log('User info init failed!');
}
The following "http.get('api/employmenttype', null, false)" means i set async to false.
I'm aware that this is probably inefficient. And i'd like to have all the calls load simultaneously.
The only problem is if i don't have them set to async false, the second part of my code might execute before the dropdowns are populated.
I've tried a couple of attempts with Jquery Deferreds, but they have resulted in what i can only describe as an abortion.
The only thing i'm looking to achieve is that the lookup calls finish before the adimport/second part of my code, in any order.... But having each call wait for the one before it to finish EG: async, seems like the only solution I'm capable of implementing decently ATM.
Would this be an appropriate place for deferred function, and could anyone point me into a direction where i could figure out how to implement it correctly, as I've never done this before?
You can use $.when to combine multiple promises to one that resolves when all of them have been fulfilled. If I got you correctly, you want
function lookupInit() {
return $.when(
http.get('api/employmenttype').done(function (response) {
console.log('loaded: employmenttype');
vm.lookups.allEmploymentTypes(response);
}),
http.get('api/actionlist').done(function (response) {
console.log('loaded: actionlist');
vm.lookups.allActionListOptions(response);
}),
http.get('api/company').done(function (response) {
console.log('loaded: company');
vm.lookups.allCompanies(response);
}),
// … some more
);
}
Then somewhere else
lookupInit(id).then(function(/* all responses if you needed them */) {
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
return http.json('api/adimport', {by:"latest"})
} else {
console.log('api/adimport: transaction');
return http.json('api/adimport', {by:"transaction", TransactionId:id});
}
}, function(err) {
console.log('User info init failed!');
}).done(viewInit);
In the Jquery API I've found this about resolving multiple deferreds:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively.
each argument is an array with the following
structure: [ data, statusText, jqXHR ] */
var data = a1[0] + a2[0]; /* a1[0] = "Whip", a2[0] = " It" */
if ( /Whip It/.test(data) ) {
alert("We got what we came for!");
}
});
Using this with your code:
var defer = $.when(
$.get('api/employmenttype'),
$.get('api/actionlist'),
$.get('api/company'),
// ... 5 more
);
defer.done(function (arg1, arg2, arg3 /*, ... 5 more*/) {
vm.lookups.allEmploymentTypes(arg1[0]);
vm.lookups.allEmploymentTypes(arg2[0]);
vm.lookups.allEmploymentTypes(arg3[0]);
// .. 5 more
vm.userInfo.BusinessUnitID('0');
vm.userInfo.BuildingCode('0');
if (id === undefined) {
console.log('api/adimport: latest');
http.json('api/adimport', { by: "latest" }, false).done(viewInit);
} else if (id !== undefined) {
console.log('api/adimport: transaction');
http.json('api/adimport', { by: "transaction", TransactionId: id }, false).done(viewInit);
}
});
You can use the defer of the $.when() inside an other $.when(), so if the json calls are not dependant on the first calls you can add them in a an onther defer.

Categories