3

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.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Muffasa
  • 218
  • 4
  • 12

3 Answers3

3

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:

  1. pass a callback into the function
  2. 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);
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
0

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!

Sam
  • 8,330
  • 2
  • 26
  • 51
  • nice try m8 but the return scope is 3rd lvl deep, it dodnt work, thanks thow! – Muffasa Jun 26 '15 at 07:36
  • 1
    For future reference, return values can be 'bubbled' up through levels in a couple of ways: `(function(){return (function(){return true})()})()`, or, if more control is needed: `(function(){if ((function(){return true})()) console.log("do something")})()`. – Sam Jun 27 '15 at 08:03
0

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.

evilunix
  • 960
  • 6
  • 11
  • I have tried that, since the call is async the flag will be set only after the new campaign addition. – Muffasa Jun 26 '15 at 07:36