Node class start function - javascript

I am new to javascript/node and I am building a class that calls the Telegram api every second to get updates and store so I can have other function use that data. I have pieced together code form examples but I am getting an error when I call bot.start(); because no function is being passed to getUpdates. I am not sure why the (fn) is needed.
class Bot {
constructor(token) {
let _baseApiURL = `https://api.telegram.org`;
this.baseApiURL = _baseApiURL;
this.token = token;
}
start(){
this.getUpdates();
}
getBaseApiUrl(){
return this.baseApiURL;
}
getToken(){
return this.token;
}
getAPI(apiName) {
return axios.get(`${this.getApiURL()}/${apiName}`);
}
getApiURL() {
return `https://api.telegram.org/bot${this.getToken()}`;
}
getUpdates(fn) {
this.getAPI('getUpdates')
.then(res => {
this.storeUpdates(res.data);
fn(res.data);
setTimeout(() => {
this.getUpdates(fn);
}, 1000);
})
.catch(err => {
console.log('::: ERROR :::', err);
});
}
storeUpdates(data){
console.log(data);
}
}
const bot = new Bot(TOKEN);
bot.start();

Its not clear what exactly you are trying to achieve with that fn method. You are not passing any method, therefore it fails. This would work
getUpdates() {
this.getAPI('getUpdates')
.then(res => {
this.storeUpdates(res.data);
setTimeout(() => {
this.getUpdates();
}, 1000);
})
.catch(err => {
console.log('::: ERROR :::', err);
});
}
If you want to implement some kind of Observer pattern, you should not couple it with getUpdates method, just create methods for registering observers and notify them when store is changed.
Also the way how you trigger the repetition is not too good, because once you get error (and with HTTP methods you usually get some ERROR sooner or later) it will break the whole flow.
Use something like https://www.npmjs.com/package/cron to trigger periodic actions.

Related

Wait for axios response before doing anything else

I have the following function in my app.js:
window.SGN = require('./core/core');
SGN.importPermissions();
which comes from a home made package:
define('SGN',
['axios'],
function (axios) {
var SGN = {
importPermissions: () => {
axios
.get(`/admin/users/permissions`)
.then((response) => {
window.SGN.permissions = Object.values(response.data);
})
.catch((error) => {
console.log(error);
})
.then(() => {
});
}
}
return SGN;
}
);
However sometimes the rest of the application is run before that axios request is finished, how can I make it so that the application always waits for the request before everything else?
Although this looks like a duplicate question, I haven't found any answer that fixes this.
The rest of the application should get informed about when the asynchronous request has completed. For that to happen you should return the promise that you have created:
var SGN = {
importPermissions: () => {
return axios
// ^^^^^^
.get(`/admin/users/permissions`)
// ...etc
The rest of the application should be made dependent on the promise returned by SGN.importPermissions(), and so it could be like this:
SGN.importPermissions().then(() => {
// Anything that depends on the request should execute here,
// or be called from here
});

How can i call an async function inside another function in a javascript class?

i am trying to call an async function inside a class but keep getting an error that this.getcategory.then is not a function
at category.js:21
I have a form which is used by the admin to add more categories into the database. categform.addEventListener('submit', e => { this line will listen for the submit event after which the data is taken and inserted into the the database using the code using the code snippet below
super.postText("../includes/getcategoy.inc.php", categformData)
.then(res => {
console.log(res);
// 1. if the results are ok fetch the fresh data from the database
this.getcategory.then(
res => {
console.log(res);
}
)
}).catch((error) => {
console.error('Error:', error);
});
now the above code returns a promise then if the result is ok i call another function in the same class got get the latest data form the database. the fuction is this.getcategory remember i am calling the function inside the then and am getting an error. the reason why i am calling inside the then is because only want it to be executed after sending of the data into the database has been resolved.
but when i call it outside the first function i do not get an error..
if you look just after the catch block i have commented it out. if i call it there i do not get an error. yet i do not want to call it there. remember the function returns a promise and it is defined as the last function in the class
how can i solve this problem, below is the whole code
import { FETCH } from "./fetch.js";
class CATEGORY extends FETCH {
constructor() {
super()
}
listencategory() {
//1. Listen for the button click
const categform = document.getElementById('categotyform');
categform.addEventListener('submit', e => {
e.preventDefault();
//2. get the data from form
const categformData = new FormData(categform);
super.postText("../includes/getcategoy.inc.php", categformData)
.then(res => {
// console.log(res);
// 1. if the results are ok fetch the fresh data from the database
this.getcategory.then(
res => {
console.log(res);
}
)
}).catch((error) => {
console.error('Error:', error);
});
});
//this.getcategory().then(res=>{
// console.log(res);
//
//})
}
async getcategory() {
try {
const results = await super.get("../includes/getcategoy.inc.php");
return results;
} catch (error) {
console.log(error);
}
}
}
const categ = new CATEGORY;
categ.listencategory();
i am trying to get data that has been returned by async getcategory()
getcategory is an Async function that should be made a function call like below.
this.getcategory().then(
res => {
console.log(res);
}
)
This link helps you create and use promises effectively.
Async functions - making promises friendly

Why is my Firestore call not resolving before going further with the next .then()?

I thought I had a simple function:
database change trigger (.onUpdate)
find out which change is possibly important for a notification (prepareNotifications(change))
ask firebase if there are records that want a notification about that change (getDatabaseNotifications(changeWithNotification))
sent notifications (sentNotifications(changeWithNotification))
I'm stuck for a couple off days now on how to resolve the Firebase call before moving on.
tried to Promise.all([getDatabaseNotifications()])
tried to chain this function like this:
changes
then firebase call
then sent notifiactions
What is happening:
I get the changes,
The call to Firebase is done,
But before waiting for the result it moves on to sending notifications.
It finds no notifications in Firebase (but there are notifications!)
It's gathering the notifications (array [])
... here I push a test notification ...
It's sending 1 notification (the test notification)
Then it resolves the Firebase notifications (this should be resolved before).
Then the function stops without doing anything anymore.
This is how my function looks now. Can someone explain how I can wait on Firebase?
exports.showUpdate = functions.firestore
.document('shows/{showId}')
.onUpdate((change, context) => {
return prepareNotifications(change) // check if and which notifications to get out of the firebase database
.then(changes => {
console.log('changes');
console.log(changes);
if(changes) {
const gatherData = [];
changes.forEach(change => {
console.log('change');
console.log(change);
getDatabaseNotifications(change.showId, change.ring, change.ringNumber) // firebase call
.then(data => {
gatherData.push([...gatherData, ...data]);
console.log('gatherData');
console.log(gatherData);
})
.catch(err => {
console.log(err);
})
})
return gatherData;
}
return null;
})
.then(notifications => {
console.log('notifications');
console.log(notifications);
notifications.push(testData); // add some test notifications
if (notifications && notifications.length > 0) {
sentNotifications(notifications); // sent notifications
return 'sending notifications';
}
return 'no notifications to sent';
})
.catch(err => {
Sentry.captureException(new Error(`Showupdate sending notifications not ok. Error message: ${err.message}`));
})
});
Updated code which works! thanks to your examples.
exports.showUpdate = functions.firestore
.document('shows/{showId}')
.onUpdate((change, context) => {
return prepareNotifications(change) // check if and which notifications to get out of the firebase database
.then(changes => {
if(changes) {
return getDbRecords(changes);
}
})
.then(notifications => {
if (notifications && notifications.length > 0) {
sentNotifications(notifications); // sent notifications
return 'sending notifications';
}
return 'no notifications to sent';
})
.catch(err => {
Sentry.captureException(new Error(`Showupdate sending notifications not ok. Error message: ${err.message}`));
})
});
function getDbRecords(changes) {
const gatherData = [];
const gatherDataPromises = [];
changes.forEach(change => {
gatherDataPromises.push(
getDatabaseNotifications(change.showId, change.ring, change.ringNumber) // firebase call
.then(data => {
gatherData.push(...data);
})
);
});
return Promise.all(gatherDataPromises)
.then(() => { return gatherData }
);
}
This section of your code doesn't handle promises properly, it creates a bunch of work but then will return gatherData before any of it has happened, which is why you don't see any notifications:
if(changes) {
const gatherData = [];
changes.forEach(change => {
console.log('change');
console.log(change);
getDatabaseNotifications(change.showId, change.ring, change.ringNumber) // firebase call
.then(data => {
gatherData.push([...gatherData, ...data]);
console.log('gatherData');
console.log(gatherData);
})
.catch(err => {
console.log(err);
})
})
return gatherData;
}
Notably, you probably want that return gatherData to be chained off the set of promises that are generated by the entire set of calls to getDatabaseNotifications.
Something like:
if(changes) {
const gatherData = [];
const gatherDataPromises = [];
changes.forEach(change => {
console.log('change');
console.log(change);
gatherDataPromises.push(
getDatabaseNotifications(change.showId, change.ring, change.ringNumber) // firebase call
.then(data => {
gatherData.push([...gatherData, ...data]);
console.log('gatherData');
console.log(gatherData);
})
);
});
return Promise.all(gatherDataPromises)
.then(() => { return gatherData });
}
I removed the catch statement to allow the error to bubble up to the top level catch.
Caution: I have not tested this, as I don't have sample data or the code for getDatabaseNotifications, but the general approach should solve your current problem. Likewise, it allows all the calls to getDatabaseNotifications to run in parallel, which should be significantly faster than just awaiting on them in sequence.
That said, you do have other problems in this code -- for example, the return null just below the block I am discussing will likely lead you into trouble when you try to use notifications.push() in the following then() (but this also appears to be test code).
I think it's because of the async nature of the methods. So, instead of waiting "getDatabaseNotifications()" to finish it's job, it jumps into ".then(notifications =>{}" and in this case gatherData returns empty.
putting await before calling the method might work.
await getDatabaseNotifications(change.showId, change.ring, change.ringNumber)

nested, depended callback functions

I am quite new to the callback hell (but i understand its meaning more and more now)
The setup:
getAccessToken: call to get an accesstoken from an api
getUserID:with access token, get a userID from an api
getUserDetails: With userID get userdetails from an api
postUserDetails: post retrieveduserdetails to an api
I need to pass values down the calls:
getAccessToken token -> getUserID(token) userID
->getUserDetails(userID) userDetails -> postUserDetails(userDetails)
in my naivity i thought i could get something running like:
postUserDetails(getUserDetails(getUserID(getAccessToken())));
or the other way round (where i would need to change the naming convention but i tried around so much that i ended up entangled in the below
getAccessToken(getUserID(getUserDetails(postUserDetails)))
What is the logical structure to get something like the below with asynchronous ajax calls working? How can I pass down multiple callbacks that get values from the previous call?
Am i relying on any framework (like async) to get a postUserDetails(getUserDetails(getUserID(getAccessToken()))) working?
I need to pass values down the calls in example getAccessToken -> getUserID ->getUserDetails -> postUserDetails I don't know if that was clear from my question
yes, values, but not the promises themselves.
a simple mock of your code:
//your call chain
getAccessToken()
.then(getUserID)
.then(getUserDetails)
.then(postUserDetails)
.then(() => {
console.log("Done");
})
//a mock of your code
//a utility to "simulate" the delay of a server call and return a Promise
function wait(delay) {
return new Promise(resolve => setTimeout(resolve, delay));
}
function getAccessToken() {
console.log("getAccessToken");
//mock the request to the server
return wait(Math.random() * 1000+500)
.then(() => {
console.log("generating a token");
return Math.random().toString(36).slice(2)
});
}
function getUserID(token) {
console.log("getUserID(%o)", token);
//mock the request to the server
return wait(Math.random() * 1000+500)
.then(() => {
console.log("returning userId");
return "id-1234";
});
}
function getUserDetails(userId) {
console.log("getUserDetails(%o)", userId);
//mock the request to the server
return wait(Math.random() * 1000+500)
.then(() => {
console.log("returning user");
return {
id: userId,
firstName: "Zaphod",
lastName: "Beeblebrox"
}
});
}
function postUserDetails(user) {
console.log("postUserDetails(%o)", user);
return wait(Math.random() * 1000+500)
.then(() => {
console.log("userDetails posted");
});
}
.as-console-wrapper{top:0;max-height:100%!important}

rxjs - understand observable and observer

I can't get my head around observable and observer (rxjs), i understand that observable can dispatch the messages to observer, and observer subscribe to observable, but I dont know how to setup this?
Lets say I would like to request URL, first time the user call "loadData", the data is loaded from http request, and saved locally inside the class, next time the user call "loadData", we dont want to load from http, but get the data locally, but I would like to use the same code "loadData", and it should return Observer, so the developed don't know where and how the data is loaded!
let data = [];
function loadData():Observer {
var observer = new Observer();
if (data.length > 0) {
var observable = new Observable.from(data);
observable.add(observer);
observable.notify();
} else {
var observable = this.http.get("data.json");
observable.add(observer);
observable.readyData( (data) => {
this.data = data;
observable.notify();
};
}
}
var observer = loadData();
observer.dataComing((data) => console.log(data));
Any explanation or link to any page would be great, I understand map filter reduce in Array etc, and also the observer pattern which is easy, but not RXJS way, it's very confusing!
Thank you very much!
Here is sample of observer / observable:
var obs = Observable.create((observer) => {
setTimeout(() => {
observer.next('some event');
}, 1000);
});
obs.subscribe((event) => {
// The event is received here
});
The observer is used to trigger an event and the observable to receive it in short. Most of time, the observer is intervalle used. For example by the HTTP support of Angular2
Here are some links regarding reactive programming:
http://slides.com/robwormald/everything-is-a-stream
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
For your particularly use case, you could use this:
loadData(url:string):Observable {
if (this.cachedData) {
return Observable.of(this.cachedData);
} else {
return this.get(...).map(res => res.map()).do((data) => {
this.cachedData = data;
});
}
}
Edit
I would refactor your code this way:
#Injectable()
export class LoungesService {
constructor(private http:Http) {
this.loungesByCity = {};
}
getLoungesByCity(city:City):Observable<any> {
if (this.loungesByCity && this.loungesByCity[city.id]) {
return Observable.of(this. loungesByCity[city.id]);
} else {
return this.http.get("lounges.json")
.map(res => <Lounge[]> res.json().data)
.map((lounges) => {
return lounges.filter((lounge) => lounge.city_id === city.id);
})
.do(data => this.loungesByCity[city.id] = data);
}
}
Note that LoungesService must be defined as shared service when bootstrapping your application:
bootstrap(AppComponent, [ LoungesService ]);

Categories