简体   繁体   中英

How to stop function execution inside nested promises

I'm a beginner when it comes to promises and I'm trying to understand how to work with them.

I have a firebase trigger where I am performing some validation. If the validation fails, I want to "exit" the trigger, meaning I don't want any code after the validation to execute (assuming the validation failed). But it does. Even though I'm sure that the validation fails (the "You have been timed out due to inactivity. Please go back to the bed booking map and start again" is sent to the android app I'm developing), the code after keeps executing. I know this because I've placed console logs inside it.

I've put comments in my code for the validation I'm talking about, and what code I don't want executed.

exports.createCharge = functions.database.ref('/customers/{userId}/charges/{id}')
  .onCreate((snap, context) => {
    console.log("Entered createharge function");
    const val = snap.val();
    return admin.database().ref(`/customers/${context.params.userId}/customer_id`)
      .once('value').then((snapshot) => {
        return snapshot.val();
      }).then((customer) => {

        // Do stuff

        if (val.type === 'beds') {
          // Check if user email is the same as bed email
          for (var i = 0; i < val.beds.length; i++) {
            var bedEmailRef = db.ref(`beds/${val.hid}/${val.beds[i]}/email`);
            bedEmailRef.on("value", function(bedEmailSnap) {
              var bedEmail = bedEmailSnap.val();
              if (val.email !== bedEmail) { // VALIDATION
                snap.ref.child('error').set("You have been timed out due to inactivity. Please go back to the bed booking map and start again"); 
                return null; // Here, I want to exit the whole function. 
              }
            });
          }

          // If val.email !== bedEmail, I NEVER want to reach here!
          return admin.database().ref(`/hotels/${val.hid}/bedPrice`)
            .once('value').then((tempBedPrice) => {
                // do stuff
              console.log("got here");
              return stripe.charges.create(charge, {idempotency_key: idempotencyKey});
            }).then((response) => {
              // Do more stuff
              return snap.ref.set(response);
            }).catch((error) => {
              snap.ref.child('error').set(userFacingMessage(error));
              return reportError(error, {user: context.params.userId});
            })

        } else throw Error('No type');
      });
  });

Why am I getting this behaviour? How can I stop the code after the validation from executing?

The problem is that you are adding a "listener" after checking for "beds" type. A solution would be to fetch data once :

 // This is a snippet from Firebase documentation
 return firebase.database().ref('/users/' + 
   userId).once('value').then(function(snapshot) {
      var username = (snapshot.val() && snapshot.val().username) || 
     'Anonymous';
     // ...
 });

Or refactor your code so your validation can be set as a callback that can be returned if some criteria is met.

Additionally, here's a link for Cloud functions best practices that can help you writing better cloud functions.

In cases where you want to exit / don't want anymore events on the listeners, refactor your code as below:

       bedEmailRef.on("value", function(bedEmailSnap) {
          var bedEmail = bedEmailSnap.val();
          if (val.email !== bedEmail) { // VALIDATION
            snap.ref.child('error').set("You have been timed out due to inactivity. Please go back to the bed booking map and start again"); 
            bedEmailRef.off('value'); // This will stop listening for further events
            return null; // This is useless because returning inside a listener has no effect
          }
        });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM