NodeJS Wait till a function has completed - javascript

I have an application where i have a Client class, that needs to be executed with different configurations for each user when i receive a message on discord(It's a chat application having its own API calls for checking for new messages etc). I have initialized the obj constructor whenever i receive a new message with new configurations for that user, but the problem is when i receive multiple messages at the same time, only the latest user's configurations are used for all the client objects created. Attaching sample code from the application:
Wait for message code:
const app = require("./Client")
const app2 = require("./MyObject")
bot.on('message', async (message) => {
let msg = message.content.toUpperCase(), pg;
let client = await new app2.MyObject();
//set all such configurations
config.a = "valuea";
// initialize my
pg = await new app.Client(config, client);
let result = await pg.Main(bot, message, params).then((result) => {
// Do stuff with result here
});
});
Client class:
class Client {
constructor(config, client) {
this.config = config;
this.client = client;
}
async Main(bot, message, params) {
let result = {};
this.client.setProperty1("prop");
await this.client.doSOmething();
result = await this.doSOmethingMore(message);
this.client.doCleanUp();
return result;
}
}
I had also tried initializing the obj constructor in the Client class, but even that fails for some reason.
Any suggestions how Can i correct my code?

You don't need to use .then and await at the same time.
bot.on('message', async (message) => {
let msg = message.content.toUpperCase();
let client = await new MyObject();
//set all such configurations
config.a = "valuea";
// initialize my
pg = new app.Client(config, client);
let result = await pg.Main(bot, message, params);
// Do stuff with result here
console.log(result);
});
(You don't need to use await at constructor call because it is not an async method)
note: async-await will work on higher version of node than 7.6
If you want to use .then:
bot.on('message', (message) => {
let msg = message.content.toUpperCase();
new MyObject().then(client => {
//set all such configurations
config.a = "valuea";
// initialize my
pg = new app.Client(config, client);
pg.Main(bot, message, params).then(result => {
// Do stuff with result here
console.log(result);
});
});
});

It's difficult to be certain, since you don't include the MyObject code, but generally it's a bad practice to return a promise from a constructor.
Further, if you don't do it right, then whatever that promise resolves to might not in fact be the Client that your calling code expects.

Related

Call method on object also do async operation before

i'm using a library that return an object with multiple keys and methods to use, one of the key of this object is accessToken.
accessToken value must exist before calling any method of this object (otherwise you're not authenticated).
The token is retrieved externally using an async axios function, also this token is only valid for 1 hour, in this case it's okay to get a new token every time you call some function inside it.
I dont' want to recreate the object every time i use this library (i'm using this library multiple times).
So far based on few articles i found online i did this:
const force = require('jsforce')
const { SFAuth } = require('./auth')
const common = require('../common')
class JSForce {
constructor (connection) {
return connection
}
static async Connect () {
const token = await SFAuth.getToken()
const connection = new force.Connection({
instanceUrl: common.SALESFORCE_URL,
accessToken: token
})
return new JSForce(connection)
}
}
const start = async () => {
const res = await JSForce.Connect()
console.log(res)
}
start()
If i try to do JSForce.Connect().sobject('Account') i get an error saying sobject is not a function.
It works if first i save JSFORCE.Connect() in a new instance and then i use this instance.sobject() but i can't do it every time i need to use it.
How would you solve this?
Thanks!!
Problem is field sobject will only come once you have JSForce connection successful. First we need to make sure we have that and we can save in variable. We will only call JSForce if we don't have instance already.
Declare a global variable in file.
let instance: <any>
// This method will return instance always if we dont have
const getInstance = async () => {
if (!instance) {
instance = await JSForce.Connect();
}
return instance;
};
const start = async () => {
const res = await getInstance().sobject('Account');
console.log(res);
}
start();

Discord JS bot how to make a recursive async function fetching from API

I am trying to make a discord bot. I want it to fetch an API every X seconds and check for new topics created.
The API JSON contains just one object, the latest post on the site. I wanted the bot to fetch every X seconds and if the latest post found has a bigger uid to send a message in a channel. If not do nothing. The uid is a number.
Tried to use the setInterval function but could not get it to work as it gave out errors that await needs to be in a top async function.
I also hardcoded the latest post uid "latest_post" and the way I saw it working is if post_id from API is higher that the hardcoded one then hardcoded one receives the value of the new one. However latest_post's value remains unchanged as the function is executed again and lates_post is hardcoded every time. Tried to declare it outside the function but came out as undefined.
This is my current almost working code
client.on('messageCreate', async (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'new') {
var latest_post = 12345; // hardcoded latest post uid
try {
const response = await fetch('API HERE').then(r => r.text());
const body = JSON.parse(response);
const obj = body.data;
const objId = obj[0].post_id;
const objAuthor = obj[0].author;
const objTitle = obj[0].title;
if (objTopic > latest_post) {
client.channels.cache.get("CHANNEL ID").send(`Found a new post by ${objAuthor}`);
var latest_post = objId; // if it fetches a post with a higher uid then latest_post receives the value of that higher uid
} else {
var d = new Date();
var n = d.toLocaleTimeString();
console.log(`No new posts yet at ${n}`); //logs "No new posts yet at 1:30 PM"
}
} catch (error) {
message.channel.send('Oops, there was an error fetching the API');
console.log(error);
}
}
});
Can anyone guide me how to transform this in a recursive function that runs automatically every X seconds.
Thanks !
After some iterations managed to make it work the way I wanted. Will leave the code below, hope it helps!
let refreshint = 10 * 1000; //10 seconds
var API = "API_HERE";
client.on('ready', async (message) => {
console.log('Bot is connected...');
var latest_post = 12345 // hardcoded latest post uid
setInterval(async function(){
try {
const response = await fetch('API').then(r => r.text());
const body = JSON.parse(response);
const obj = body.data;
const objId = obj[0].post_id;
const objAuthor = obj[0].author;
const objTitle = obj[0].title;
if (objTopic > latest_post)
client.channels.cache.get("CHANNEL ID").send(`Found a new post by ${objAuthor}`);
console.log(`Found a new post by ${objAuthor}`);
function change_value(latest_post){
return latest_post;
}
latest_post = change_value(topicId);
} else {
var d = new Date();
var n = d.toLocaleTimeString();
console.log(`No new thread yet at ${n}`);
}
} catch (error) {
message.channels.cache.get('channel ID here').send('Oops, there was an error fetching the API');
console.log(error);
}
}, refreshint) });

How to get make asyc get with CognitoIdentityCredentials

After declaring the cred object with new AWS.CognitoIdentityCredentials I go ahead and use .get method to get the credentials and store them in IdentityId variable. When I run this function, the IdentityId variable is show to be undefined. It seems the
console.log("...IdentityId:", IdentityId); is evaluated before the credenitals are queried.
Is there a way I could use await to get the the credentials first and then move to the console.log command ?
async function GetCredentials(email, password, callback) {
let response = await signIn(email, password);
console.log("...response:", response);
let idToken = response.response.token.idToken;
let logins = {};
logins[
"cognito-idp." + AWS_REGION + ".amazonaws.com/" + AWS_COGNITO_USER_POOL_ID
] = idToken;
console.log("...logins:", logins);
let creds = new AWS.CognitoIdentityCredentials({
IdentityPoolId: AWS_COGNITO_IDENTITY_POOL_ID,
Logins: logins
});
console.log("...creds:", creds);
let IdentityId;
creds.get(function(err) {
console.log("--------------------------")
console.log('creds.data:', creds.data);
IdentityId = creds.data.IdentityId;
console.log("--------------------------")
});
}
You could try
const promise = new Promise((resolve) => {
creds.get(function(err) {
console.log("--------------------------")
console.log('creds.data:', creds.data);
const IdentityId = creds.data.IdentityId;
resolve(IdentityId)
console.log("--------------------------")
});
})
const returned = await promise
But I'm not sure where exactly you're using IdentityId because your statement
It seems the console.log("...IdentityId:", IdentityId); is evaluated before the credenitals are queried.
doesn't exist in your code

How to let a webworker do multiple tasks simultaneously?

I am trying to let a Web-Worker manage its state, meanwhile serving multiple async requests.
worker.ts file
let a =0; //this is my worker's state
let worker=self as unknown as Worker;
worker.onmessage =(e)=>{
console.log("Rec msg", e.data);
if(e.data === "+1"){
setTimeout(()=>{
a=a+1;
worker.postMessage(a);
},3000);
}else if(e.data=== "+2"){
setTimeout(()=>{
a=a+2;
worker.postMessage(a);
},1000)
}
}
And this is my main file: main.ts
let w =new Worker("./worker.ts", {type: "module"})
let wf =async (op: string)=>{
w.postMessage(op);
return new Promise<any>((res,rej)=>{
w.onmessage=res;
});
}
(async()=>{
let f1 = await wf("+1");
console.log("f1",f1.data);
})();
(async()=>{
let f2 = await wf("+2");
console.log("f2",f2.data);
})()
Only f2 is returned , and f1 is lost.
I have used timeouts to simulate say some async task done by worker themselves.
How do I receive both f1 and f2?
Your problem is that you are trying to take an event based API and use it as a Promise based one, but events may fire multiple times, while Promise should resolve only once.
The communication between the Worker and the main thread works by sending and receiving messages, but there is by default no one-to-one relation between these messages. Both ends of the communication (ports) will simply stack incoming messages, and handle them sequentially, when they'll get time.
In your code, the main thread's worker.onmessage handler of f1 has been overwritten by the second call f2 synchronously (one microtask later, but that's still synchronous for our matter).
You could attach your event using the addEventListener method, at least this way it wouldn't be overwritten. But even then, when the first message event will fire on worker, both handlers will think it's there own message that did arrive, while in fact it was the one of f2. so that's not what you need...
What you need is to set up a protocol of communication which would allow both ends to identify each task. You could for instance wrap all your tasks' data with an object containing a .UIID member, be sure both ends wraps their message this way, and then from main thread check that UUID to resolve the appropriate Promise.
But that can become a bit complicated to implement and to use.
My personal favorite way is to create a new MessageChannel per task. If you don't know this API, I invite you to read this answer of mine explaining the basics.
Since we are sure the only one message that will come through this MessageChannel is the response from the Worker to the one task we sent to it, we can await it just like a Promise.
All we have to do, is to make sure that in the Worker thread we respond through the transferred port instead of the global scope.
const url = getWorkerURL();
const worker = new Worker(url)
const workerFunc = (op) => {
// we create a new MessageChannel
const channel = new MessageChannel();
// we transfer one of its ports to the Worker thread
worker.postMessage(op, [channel.port1]);
return new Promise((res,rej) => {
// we listen for a message from the remaining port of our MessageChannel
channel.port2.onmessage = (evt) => res(evt.data);
});
}
(async () => {
const f1 = await workerFunc("+1");
console.log("f1", f1);
})();
(async () => {
const f2 = await workerFunc("+2");
console.log("f2", f2);
})()
// SO only
function getWorkerURL() {
const elem = document.querySelector( '[type="worker-script"]' );
const script = elem.textContent;
const blob = new Blob( [script], { type: "text/javascript" } );
return URL.createObjectURL( blob );
}
<script type="worker-script">
let a = 0;
const worker = self;
worker.onmessage = (evt) => {
const port = evt.ports[0]; // this is where we will respond
if (evt.data === "+1") {
setTimeout(() => {
a = a + 1;
// we respond through the 'port'
port.postMessage(a);
}, 3000);
}
else if (evt.data === "+2") {
setTimeout(() => {
a = a + 2;
// we respond through the 'port'
port.postMessage(a);
}, 1000)
}
};
</script>

Firebase Functions: Why do they sometimes fail? Why do they often complete without error but don't fulfill all tasks?

This perplexes me. I'm six months into a firebase project and have been using Javascript for firebase-functions. I've learned a lot along the way by adding transactions, promises, batch writes and neat tricks. However, it seems like complete luck for a function to execute correctly. More often than not, the functions do execute correctly, but there are strange periods when bursts of consecutive function calls where functions half complete with no errors in the logs.
For example. I have a function for when a new user joins my app. It does a little bit of server data construction and also notifies the two admins that a new user has joined. Last night I did a test run with two new users and got no notification, but their user profiles constructed correctly on the server database. I checked the function logs and there were no errors.
Am I not handling Promises in the correct way? If a firebase function hangs, does it mess up the next few function calls?
exports.onNewUser = functions.firestore
.document('/users/{userId}')
.onCreate(async (snapshot, context) => {
user = snapshot.data().username;
//Notification payload
const payload = {
notification: {
title: `New user!`,
body: `${user} has joined [AppName]`
}
};
var promises = [];
//Check if usename unique
var passed = true;
promises.push(db.runTransaction(async t => {
const docRef = db.collection('users').doc('index');
const doc = await t.get(docRef);
var newIndex = doc.data().usernames;
if (newIndex[user.toUpperCase()] == true) {
t.delete(snapshot.ref);
passed = false;
return null;
} else {
newIndex[user.toUpperCase()] = true;
t.set(docRef, { 'usernames': newIndex });
}
}));
if (!passed) return Promise.all(promises);
//add new user to Algolia database
const algoliasearch = require('algoliasearch');
const algoliaClient = algoliasearch(functions.config().algolia.appid, functions.config().algolia.apikey);
const collectionIndex = algoliaClient.initIndex(collectionIndexName);
await saveDocumentInAlgolia(snapshot, collectionIndex);
//Notify Admins
db.collection('notificationTokens')
.doc(admin1)
.get().then((doc) => {
if (doc.exists && doc.data().notificationToken != null)
promises.push(pushNotification(doc.data().notificationToken, payload));
});
db.collection('notificationTokens')
.doc(admin2)
.get().then((doc) => {
if (doc.exists && doc.data().notificationToken != null)
promises.push(pushNotification(doc.data().notificationToken, payload));
});
return Promise.all(promises);
});
Just change
return Promise.all(promises);
to
return await Promise.all(promises);
You have to wait till the promises resolve before you return the function, as that would stop the instance of the cloud function.

Categories

Resources