Is there any way to break Javascript function within an anonymous function? - javascript

I just want to check if the data I'm about to insert allready exists on my Firebase, and if so I just want to break the add function:
FBDB.addCampain=function (campain){
CampiansRef.once('value',function(snapshot){
snapshot.forEach(function(childSnapshot){
if(campain.Name==childSnapshot.val().Name){
console.log("campain allredy exists on the DB");
return false; //I want to break the addCampain function from here!
}
});
});
var newCampainRef = CampiansRef.push();
campain.id = newCampainRef.key();
newCampainRef.set(campain,function(error){
if(error){
console.log("an error occured the campain did not add to the DB, error:" ,+error);
return false;
}
else{
console.log("campain succssesfuly added to the DB");
return true;
}
});
};
What currently happens is that even if the campaign exists on the database it still continues to the actual adding code. There must be a way to "break" the addCampain function within an anonymous function inside it, or even pass the "return false" up to the main scope.

If you add a few console.log statements, you'll be able to see how your code flows:
console.log('1. starting call to Firebase');
CampaignsRef.once('value',function(snapshot){
console.log('3. for value from Firebase');
snapshot.forEach(function(childSnapshot){
console.log('4. inside childSnapshot');
if (campaign.Name==childSnapshot.val().Name){
console.log("campaign already exists on the DB");
return false;
}
console.log('5. done with snapshot.forEach');
});
});
console.log('2. we started the call to Firebase');
The output will look like:
1. starting call to Firebase
2. we started the call to Firebase
3. for value from Firebase
4. inside childSnapshot
4. inside childSnapshot
4. inside childSnapshot
5. done with snapshot.forEach
This is probably not entirely what you expected. 2. is at the end of the code block, but it fires right after 1. which is at the start. This is because on starts an asynchronous load of the data from Firebase. And since this takes time, the browser continues with the code after the block. Once the data is downloaded from Firebase's servers, it will invoke the callback and you can do what you want. But by then, the original context has finished.
There is no way in JavaScript to wait for an asynchronous function to finish. While you might appreciate if such a way existed, your users would be frustrated by the fact that their browser locks up while your call to Firebase is out.
Instead you have two options:
pass a callback into the function
return a promise
I'm going to use option 1 below, because it is what the Firebase JavaScript SDK already does.
FBDB.addCampaign=function (campaign, callback){
CampaignsRef.once('value',function(snapshot){
var isExisting = snapshot.forEach(function(childSnapshot){
if(campaign.Name==childSnapshot.val().Name){
return true; // this cancels the enumeration and returns true to indicate the campaign already exists
}
});
callback(isExisting);
});
};
You'd invoke this like:
FB.addCampaign(campaign, function(isExisting) {
console.log(isExisting ? 'The campaign already existed' : 'The campaign was added');
};
Note that loading all campaigns from the server to check if a specific campaign name already exists is pretty wasteful. If you want campaign names to be unique, you might as well store the campaigns by name.
CampaignsRef.child(campaign.Name).set(campaign);

From the Firebase documentation, snapshot.forEach returns true if your callback function "cancels the enumeration by returning true". So just change your return false to return true!

Set a "global" (to your addCampaign function) flag in the forEach loop just before breaking out of it, then check this flag when you get back into your main function and return if it is set.

Related

Callback function confusion

function print(){
console.log("hello");
}
Fruit.find(function(err,fruits){
mongoose.connection.close();
if(err){
console.log(err);
}else{
// console.log(fruits);
fruits.forEach(function(fruit){
console.log(fruit.name);
})
}
})
Just a background :
the fruits model has a array of objects (fruits) and each fruit has a name with the code above I am displaying only its name
I am confused abut how we are closing mongoose connection even before logging the text also read at a article that its a callback function so gets executed after completion after execution of all steps so another question is how I know wether its a callback or a normal function also if I had another function print to make it callback (for the loop) how do I make it i.e print Hello after logging all the name of fruits but writing before log statement of fruits name
I am confused abut how we are closing mongoose connection even before logging the text
The callback function gets called when the data has been read from the mongoose connection.
The data is passed to the fruits argument.
You don't need the connection to be open after the data is already in a local variable.
how I know wether its a callback or a normal function
Callback functions are normal functions. It is just how they are used. See MDN:
A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

Changing the value of a property of an object inside a function in java script doesn't work as it should

I have two modules and in the first one I declare an object because I know that primitives are passed by value in java script and objects by reference.I want to get the response status from a request and I am passing the object as a reference so I will be able to modify its property.The problem is that it doesn't do anything.In the end the value would be the same.
//this code is in a different module from the other one
var variableToBeChanged = { something : "Initial value" };
anotherModule.changeValue(variableToBeChanged);
alert(variableToBeChanged.something);
//and in the other module I have a $.ajax and I want to get the response status(ex. 200)
//the code is sth like this:
function AnotherModule(ajax){
function changeValue(variableToBeChanged){
...
...
...
$.ajax({
...
...
...
success: function(data,xhr){
variableTobechanged.something = xhr.status;
}
});
}
}
In the end it will display: "Initial value" instead of 200 or anything else.
What am I doing wrong here?
The ajax call is asynchronous and therefore the alert gets called before the variable is modified. You can use promise in ES6 like this to make sure it is executed after ajax call completes.
new Promise((resolve) =>{
anotherModule.changeValue(variableToBeChanged);
resolve();
}).then((res) =>{
alert(variableToBeChanged.something);
}).catch((error) =>{
alert(error);
});
In javascript copy of reference to object is passed.
This means that any changes made to the object will be visible to you after the function is done executing.
Since javascript is asynchronous , alert(variableToBeChanged.something) this line gets executed before your function returns . Therefore you see old value . You have to use callbacks or promise to work synchronously.
Please refer to this question javascript pass object as reference .It explains this concept beautifully.

How to understand asynchronous Meteor.call on the client side

I'm working on a Meteor project and want to get the return value of Meteor.call in template helpers on client side. At very first, I just set a variable in the call back function and get the variable's value outside the Meteor.call. I found out the code after Meteor.call doesn't execute at all. Then I searched a bit and use Session, it works. But I don't really understand the reason. Here's my original code and modified code. Can anyone explain a bit for me? Thanks!!
Original wrong code: html
<div id="text-result-main">
<h2>{{title}}</h2>
</div>
js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
console.log(titles);
return titles;
}});
Collection text.js
Text = new Mongo.Collection("text");
Meteor.methods({
'getTitle': function(myindex){
return Text.findOne({index: myindex}).title;
}});
The working code: js
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
Meteor.call('getTitle', index,function(error, result){
Session.set("titles",result);
});
console.log(Session.get("titles"));
return Session.get("titles");
}});
Notice that I didn't publish Collection Text to the client at all because it's just so huge. Every time when I refresh the page when running the wrong code, I can't see the content of "title" or see it on the console. But when I set the session, it works. I don't really understand how it works here. Thanks
There is two issues Asynchronicity and Reactivity
This affectation
Meteor.call('getTitle', index,function(error, result){
titles = result;
});
inside the meteor call is executed but in a asynch way. So the return of your helper is immediately called, and return a empty value.
Try it out in the console of your browser.
But then, why your template render correctly with {{title}} when you use a Session Variable ?
It's because the Session is a reactive data source, witch means that every change to it trigger a re-computation of all templates involving this piece of data.
Here is a timeline:
Methods is called
Return empty value
Method is executed, setting variable value
If the Variable is a reactive data source, template is re-computed. ( in your case, the session is a reactive data source. )
To go further
I would use a reactive var in that case, it's very close from a session variable, but the scope is limited to a template.
A good read on Reactive data source: http://richsilv.github.io/meteor/meteor-reactive-data-types/
The problem is the fact that Meteor.call() is asynchronous when paired with a callback.
So when title() starts executing, it does not wait for your Meteor.call() invocation to return a result (or possibly an error). It continues execution. This is called asynchronous execution.
In short, you are trying to log the value for the key titles which doesn't exist in Session (since the state of your asynchronous Meteor call is unknown, at this point of time).
Try moving the console log statement into the callback paired with your Meteor.call() and you can see the result once it has successfully been set in Session.
A workaround to your problem is to make your Meteor.call() synchronous like this:
Template.texts.helpers({
title: function(){
var index = Router.current().params.index;
var result = Meteor.call('getTitle', index); // <--- this is synchronous code now
Session.set("titles",result);
console.log(Session.get("titles"));
return Session.get("titles");
}});
Removing the callback makes Meteor.call() behave synchronously.
If you do not pass a callback on the server, the method invocation
will block until the method is complete. It will eventually return the
return value of the method, or it will throw an exception if the
method threw an exception.
(from http://docs.meteor.com/api/methods.html#Meteor-call)
Why not use something like this:
title: function(){
var index = Router.current().params.index;
var a = Text.findOne({index: myindex}).title;
console.log(a);
return a;
without methods

Alternative for Javascript this object

I have a simple function which routes a HTTP query pattern, queries redis and sends a response. The following is the code
router.get('/getinfo/:teamname', function main(teamname) {
rclient.hgetall(teamname,function(err,obj){
console.log("the response from redis is ",obj)
cache.put(eventname,obj);
console.log("inserting to cache");
this.res.end(obj); // this object is root cause for all problems
});
}
The router object afaik, sends the response using this.res.end(obj) . I guess since I am trying to do this inside my redis client , I am getting error. Is there any other way to send the value as a response ? I thought of using emitter based model where the channel emits the response and listener gets it. but it feels like a round about way to solving this problem. Is there any simpler approach ?
The error may be because, where you're trying to use this, it doesn't have the intended value -- an object with a res property that in turn has an end() method.
That would be because every function in JavaScript has its own this with its own value. And, when nesting functions, using this will return the value for the closest function (i.e. shadowing).
To resolve that, you can save the intended value to a local variable:
router.get('/getinfo/:teamname', function main(teamname) {
var request = this;
rclient.hgetall(teamname,function(err,obj){
// ...
request.res.end(obj);
});
});
Or, bind the anonymous callback so both functions are forced to have the same this value:
router.get('/getinfo/:teamname', function main(teamname) {
rclient.hgetall(teamname, function(err,obj){
// ...
this.res.end(obj);
}.bind(this));
});

javascript can't assign variable values due to asynchronous function.

In my JavaScript code below, I cannot get user_likes to take the value of response. I can Console output "response", and it has the values I need. However, when I try to Console output user_likes, I get undefined. What gives?
function fqlUserLikes(user_id) {
var user_likes;
FB.api(
{
method: 'fql.query',
query: 'SELECT page_id,name,type FROM page WHERE page_id IN (SELECT page_id FROM page_fan WHERE uid=' + user_id + ')'
},
function(response) {
user_likes=response;
}
);
Console.log(user_likes);
return user_likes;
}
Thanks for any help,
I.N.
Your method is asynchronous, so when you try to log and return the variable, it hasn't actually been assigned yet. Take the below snippet:
//Do what you need to do in here!
function(response) {
user_likes=response;
});
//The below code is executed before the callback above.
//Also, it should be console, and not Console. JS is case sensi.
Console.log(user_likes);
return user_likes;
Your function that changes the value of user_likes is not called immediately. FB.api() starts the process of calling the API, and then returns. Your console.log() call happens at this point, and then your function returns the user_likes value to its caller. Then, at some later point, the api call completes, your callback function is called, and it sets user_likes to the appropriate value. Unfortunately, there's nothing left to read that value.
You need to completely restructure your code to make it work. Javascript doesn't really support waiting for things to happen in the middle of code; it's a very asynchronous language. The best way is if you pass a callback function that actually uses the new value, rather than trying to store it in a variable for something else to read.

Categories