Maximum call stack size exceeded with 2 Collection.find - javascript

I'm using Angular2 and Meteor. I have an exception(Maximum call stack size exceeded) in my Chrome console since I added that code in my client side:
var userChannelsId = UserChannels.find({idUser: Meteor.userId()});
this.channels = Channels.find({_id: {$in: userChannelsId}});
From what I found, this exception occurs when there is an infinite loop. I'm sure the error comes from this lines (I debugged my code until I was sure).
Is the problem coming from the fact that the first search isn't finished when the second begin ?
All function :
getChannelList(): void {
console.log('ok');
var userChannelsId = UserChannels.find({idUser: Meteor.userId()});
this.channels = Channels.find({_id: {$in: userChannelsId}});
console.log('ko');
this.channels.subscribe(channel => {
this.selectChannel(channel[0]);
});
}
EDIT:
As #MichaelSolati I tried to get only the IdChannel of the userChannel, but I still have the same error ... It returns an observable and not an array. Maybe it's the problem? This is what I did:
getChannelList(): void {
console.log('ok');
let userId = Meteor.userId();
var userChannelsId = UserChannels.find({idUser: userId});
var values = userChannelsId.map((userChannels:Array<UserChannel>) => {
let result:Array<String> = [];
if (userChannels) {
userChannels.forEach((userChan) => {
result.push(userChan.idChannel);
});
}
return result;
});
this.channels = Channels.find({_id: {$in: values}});
console.log('ko');
this.channels.subscribe(channel => { this.selectChannel(channel[0]); });
}

Updated because of the use of MeteorRxJS for the queries. While it's likely not perfect, it should probably put you on the right path.
private userChannelsSub: Subscription;
getChannelList(): void {
console.log('ok');
let userId = Meteor.userId():
let this.userChannelsSub = UserChannels.find({idUser: userId}).subscribe((userChannels: any[]) => {
if (Array.isArray(userChannels)) {
let userChannelsId = userChannels.map((channel: any) => { return channel._id; });
if (this.channels) { this.channels.unsubscribe(); }
this.channels = Channels.find({_id: {$in: userChannelsId}}).subscribe((channels: any[]) => {
this.selectChannel(channels[0]);
});
}
});
console.log('ko');
}

Alright guys,
I found a solution that works pretty well !
If you have any suggestion to improve my proposition, fill free to comment, I would appreciate it :)
getChannelList(): void {
let userId = Meteor.userId();
UserChannels.find({idUser:userId}).subscribe((userChannel:UserChannel[]) =>{
var channelsId:Array<String> = [];
userChannel.forEach((userChan)=>{
channelsId.push(userChan.idChannel);
});
this.channels = Channels.find({_id: {$in: channelsId}});
});
if(this.channels !== undefined) {
this.channels.subscribe(channel => {
this.selectChannel(channel[0]);
});
}
}
Thank you all for your answers.

Related

how to unsubscribe of function in method?

Working on dialog component with angular js and now I find out that my function is subscribed and in if condition do not quit method, but continuously executing another function afterClosed() , here is example of code :
openCreateNewContentDialog(): void {
const oldData = this.dataSource.data;
const dialogConfig = AppConstants.matDialogConfig();
const dialog = this.dialog.open(LicenseDialogComponent, dialogConfig);
dialog.beforeClosed().subscribe(licenceDate => {
for (const datesToCheck of oldData) {
const newDateFrom = new Date(licenceDate.expirationDateFrom);
const oldDateTo = new Date(datesToCheck.expirationDateTo.toString());
if (newDateFrom <= oldDateTo) {
// console.log('return?');
return;
}
}
});
dialog.afterClosed().subscribe(licence => {
if (licence) {
this._value.push(licence);
this.dataSource.data = this.value;
this.change();
}
});
}
What is the best and optimized way to unsubscribe beforeClosed() function?
So from your description, I understand that you dont want a second subscription to happen if the condition in the first subscriber is true, right? But you subscription will happen anyway because you instantiated it in the method, the code in the subscribe() it's just a callback. So if you dont want a lot of rewriting I will suggest storing
subscriptions in variables, so you will have an access to them and can unsubscribe at any time.
openCreateNewContentDialog(): void {
const oldData = this.dataSource.data;
const dialogConfig = AppConstants.matDialogConfig();
const dialog = this.dialog.open(LicenseDialogComponent, dialogConfig);
const beforeClosed = dialog.beforeClosed().subscribe(licenceDate => {
for (const datesToCheck of oldData) {
const newDateFrom = new Date(licenceDate.expirationDateFrom);
const oldDateTo = new Date(datesToCheck.expirationDateTo.toString());
if (newDateFrom <= oldDateTo) {
// console.log('return?');
afterClosed.unsubscribe();
return;
}
}
});
const afterClosed = dialog.afterClosed().subscribe(licence => {
if (licence) {
this._value.push(licence);
this.dataSource.data = this.value;
this.change();
}
});
}
I hope it helps! Also you can try https://www.digitalocean.com/community/tutorials/angular-takeuntil-rxjs-unsubscribe if you have to handle multiple subscriptions.

Cannot read property 'length' of undefined...Although the object is defined

First I have the following code section:
export default class Recipe {
constructor(recID) {
this.RecipeID = recID;
}
async GetRecipe() {
try {
let Result = await Axios(
`https://forkify-api.herokuapp.com/api/get?rId=${this.RecipeID}`
);
this.Tilte = Result.data.recipe.title;
this.Author = Result.data.recipe.publisher;
this.Image = Result.data.recipe.image_url;
this.Url = Result.data.recipe.source_url;
this.Ingredients = Result.data.recipe.ingredients;
this.PublisherUrl = Result.data.recipe.publisher_url;
this.Rank = Result.data.recipe.social_rank;
} catch (error) {
alert(error);
}
}
CalculateTime() {
try {
this.Time = Math.ceil(this.Ingredients.length / 3) * 15; // error is here
} catch (error) {
console.log(this.RecipeID + ": Length Error->"+error);
}
}
}
Then I call the above code in a separate file like:
import Recipe from "./Recipe";
const RecipeController = async () => {
const ID = window.location.hash.replace("#", "");
if (ID) {
AppState.Recipe = new Recipe(ID);
try {
await AppState.Recipe.GetRecipe();
AppState.Recipe.CalculateTime();
console.log(AppState.Recipe);
} catch (error) {
alert(error);
}
}
};
Now as shown in the following image, that I do get the response of the request & promised is resolved plus there are elements in the 'ingredients' array but sometimes I still get the error "cannot read property 'length' of undefined" when I call the 'CalculateTime()' although the array is now defined and sometimes I don't get any error & it works perfectly fine.Why this random behavior? Even the IDs in the JSON response & the error I logged match i.e. 47746.
This is one reason why having too many try/catches can obscure the causes of errors, making debugging difficult. The problem can be reduced to the following:
class Recipe {
constructor(recID) {
this.RecipeID = recID;
}
async GetRecipe() {
let Result = await fetch(
`https://forkify-api.herokuapp.com/api/get?rId=47746`
).then(res => res.json());
console.log(Result); // <----- look at this log
this.Tilte = Result.data.recipe.title;
// on the above line, the error is thrown
// Cannot read property 'recipe' of undefined
}
}
const r = new Recipe();
r.GetRecipe();
See the log: your Result object does not have a .data property, so referencing Result.data.recipe throws an error. Try Result.recipe instead:
class Recipe {
constructor(recID) {
this.RecipeID = recID;
}
async GetRecipe() {
let Result = await fetch(
`https://forkify-api.herokuapp.com/api/get?rId=47746`
).then(res => res.json());
const { recipe } = Result;
this.Tilte = recipe.title;
this.Author = recipe.publisher;
this.Image = recipe.image_url;
this.Url = recipe.source_url;
this.Ingredients = recipe.ingredients;
this.PublisherUrl = recipe.publisher_url;
this.Rank = recipe.social_rank;
}
CalculateTime() {
this.Time = Math.ceil(this.Ingredients.length / 3) * 15; // error is here
console.log('got time', this.Time);
}
}
(async () => {
const r = new Recipe();
await r.GetRecipe();
r.CalculateTime();
})();
Unless you can actually handle an error at a particular point, it's usually good to allow the error to percolate upwards to the caller, so that the caller can see that there was an error, and handle it if it can. Consider changing your original code so that RecipeController (and only RecipeController) can see errors and deal with them - you can remove the try/catches from the Recipe.
Are you sure some of the responses are not missing ingredients? And calculateTime is always called after getRecipe?
I would add a condition or fallback to prevent errors, as in.
this.Time = Math.ceil((this.Ingredients || []).length / 3) * 15;
I would speculate that this "random" behavior can be related to asynchronous code. You need to ensure that the class has Ingredients in place before you calculate. I have a feeling that you should try changing your syntax to a Promise handling using .then() and .catch(), especially since you already use try/catch in your code. This approach will ensure proper resolution of Promise on axios request, and will eliminate "randomness", because you will have a better control over different stages of Promise processing.
let Result = await Axios(
`https://forkify-api.herokuapp.com/api/get?rId=${this.RecipeID}`
)
.then((data) => {
this.Tilte = data.data.recipe.title;
this.Author = data.data.recipe.publisher;
this.Image = data.data.recipe.image_url;
this.Url = data.data.recipe.source_url;
this.Ingredients = data.data.recipe.ingredients;
this.PublisherUrl = data.data.recipe.publisher_url;
this.Rank = data.data.recipe.social_rank;
this.Ingerdients = data.data.recipe.ingredient;
}
.catch((err) => {
console.log(err);
return null;
});

How to fix variable is not defined` - The variable in question is clearly defined in my code

Hey amazing people of the Internet,
Pretty noobish question here, but I'm having a bit of a problem with some of this Javascript I wrote. Here's the code -
const req0 = http.request({
hostname: `api.trello.com`,
port: 443,
path: `/1/lists/5cb77e9ef180dd2bbb25acf2/cards?key=${trelloKey}&token=${trelloToken}&fields=name`,
method: `GET`
}, res => {
let str = '';
res.on('data', chunk => {
str += chunk;
});
res.on('end', () => {
try {
var level0Data = JSON.parse(str);
console.log(`Level 0: ${level0Data}`)
level0Data.forEach((k,v) => {
var id = k.id;
var name = k.name;
console.log(`${id} = ${name}`);
});
} catch(error) {
console.log(error);
}
});
});
req0.on('error', error => {
console.log(error);
});
req0.end();
level0Data.forEach(function(level0Names) {
// code here
});
The issue I'm having with this is that it keeps giving me an error stating that the level0Data is not defined.
I know it's a pretty newbie question, but I'd appreciate some guidance with it.
Thanks!
Do you see that level0Data is not defined in below scope? (MDN reference(Scope))
level0Data.forEach(function(level0Names) {
// code here
});
You must use level0data inside of callback function.
// NOTE: Level0data is available only here, as you defined in this scope.
...
var level0Data = JSON.parse(str);
console.log(`Level 0: ${level0Data}`)
level0Data.forEach((k,v) => {
var id = k.id;
var name = k.name;
console.log(`${id} = ${name}`);
});
...
Or you can pass a function to handle callback.
function handle(str) {
var level0Data = JSON.parse(str);
console.log(`Level 0: ${level0Data}`)
level0Data.forEach((k,v) => {
var id = k.id;
var name = k.name;
console.log(`${id} = ${name}`);
});
level0Data.forEach(function(level0Names) {
// code here
});
}
...
try {
handle(str)
} catch(e) {
console.error(e)
}

Javascript: How to check if async operation is still pending / In progress?

I would like to know if it is somehow possible to check if an asynchronous operation in Javascript is still pending..
Because I am doing a database request on calling a specific URL... While the db call is still in progress, I want to stop any other incoming db-calls (which means, stop any further calls to that URL in case the db-request is still pending).
Is that somehow possible?
Because the database call takes up to minutes, and I don't want to launch another database-call while the first is still in progress.. The problem is, I somehow cannot figure out how to check if the call has started and is still in progress, because the response comes only after the .then() clause when the process has already finished.
this is my db-call function:
const getWriteIndex = async () => {
return Promise.all(someFunction1, someFunction2...).then(...) {
writeMessageObject = checkDocuments(...);
return Promise.resolve(writeMessageObject);
})).catch((err) => {
return Promise.reject(err);
});
}
This is my URL/Route Call function with express:
router.get("/v1/...", someMiddleware(), async function(req,res,next) {
if (read_cached() && initialised_read) {
res.setHeader('Content-Type', 'application/json');
res.json(readmsg_obj);
} else {
try {
//HOW CAN I CHECK HERE IF THE DB-CALL IS ALREADY IN PROGRESS?
readmsg_obj.message = '';
getReadIndex().then((message) => {
initialised_read = true;
readmsg_obj = {...message};
res.setHeader('Content-Type', 'application/json');
res.json(readmsg_obj);
}).catch((reject) => {
logger.error(`/../... : ${reject}`);
initialised_read = false;
res.status(500).send(reject);
});
} catch(err) {
logger.error(`/v1/... : ${err}`);
res.status(500).send(err);
};
}
});
hmm I found a workaround here:
https://ourcodeworld.com/articles/read/317/how-to-check-if-a-javascript-promise-has-been-fulfilled-rejected-or-resolved
so I wrote that function to check for promise stati, but I am still wondering if it's not somehow possible to query for static promise properties to get their actual state ;) (but weirdly, I didn't find any on the web).
const checkPendingRequest= (promise) => {
if (promise.isResolved) return promise;
// Set initial state
var isPending = true;
var isRejected = false;
var isFulfilled = false;
// Observe the promise, saving the fulfillment in a closure scope.
var result = promise.then(
function(v) {
isFulfilled = true;
isPending = false;
return v;
},
function(e) {
isRejected = true;
isPending = false;
throw e;
}
);
result.isFulfilled = function() { return isFulfilled; };
result.isPending = function() { return isPending; };
result.isRejected = function() { return isRejected; };
return result;
}
So I fixed my function for the request:
router.get("/v1/...", someMiddleware(), async function(req,res,next) {
if (read_cached() && initialised_read) {
res.setHeader('Content-Type', 'application/json');
res.json(readmsg_obj);
} else {
try {
readmsg_obj.message = '';
if ((dbQueryPromiseRead != null) && dbQueryPromiseRead.isPending()) {
logger.info(`Database request for Index-read is still pending!`);
return;
}
dbQueryPromiseRead = checkPendingRequest(getReadIndex());
dbQueryPromiseRead.then((message) => {
initialised_read = true;
readmsg_obj = {...message};
res.setHeader('Content-Type', 'application/json');
res.json(readmsg_obj);
}).catch((reject) => {
logger.error(`/../... : ${reject}`);
initialised_read = false;
res.status(500).send(reject);
});
} catch(err) {
logger.error(`/v1/... : ${err}`);
res.status(500).send(err);
};
}
});
You need to try add in node.js like global.dbCallState flag if operation is still running.
This global var one for all modules.
Do not change this object like global = new Object();, but you can use child field's.
https://nodejs.org/api/globals.html
You can change it in another module like global.dbCallState = false.
It not best solution, but it can help.
But i don't know, why you want only one connection. Its not good solution to block I/O in node.js

Object values to array, then replace, not a function?

So I am using Vue.js, which is not the problem I know, it is just something I'm doing wrong, and there is a reason I'm doing it this way, but I keep getting an error like its not an array.
Code:
methods: {
async register() {
this.errors = '';
this.success = '';
await RegisterService.insertUser(this.email, this.username, this.password, this.passwordTwo)
.then(data => {
if (data.data) {
let v = Object.values(data.data);
console.log(v);
v = v.replace(/["[]]/g, '');
this.errors = v;
} else {
this.success = `Registration Successful!`;
}
});
}
}
So what I am getting is v.replace is not a function, I'm sure there is a simple reason for this and I am just whiffing at figuring it out.

Categories