1

I'm trying to write some asynchronous code in JavaScript, especially I want the code to be executed after a certain functions has finished the execution.

Below is my code:

 function triggerFunction(duration) {
    var db = firebase.firestore();

    db.collection('users').get().then((querySnapshot) => {
        querySnapshot.forEach((doc) => {
          var data=doc.data();
          db.collection('users').doc(doc.id).collection('#aName').get().then((querySnapshot1) => {
                querySnapshot1.forEach((doc1) => {
                  var firstData = doc1.data()
                  var newID = createRandomId()
                  var alreadyExist = checkAlreadyElementDB(firstData.appType, doc.id)
                  if(firstData.open == true && !alreadyExist){
                      console.log("Entered here: " + alreadyExist)
                      db.collection('#name').doc(newID).set({
                        #NotImportant
                      })
                      db.collection('users').doc(doc.id).collection('#aName').doc(doc1.id).update({
                        open: false
                      })
                  } else if(firstData.open == true && alreadyExist){
                        db.collection('users').doc(doc.id).collection('#aName').doc(doc1.id).update({
                          open: false
                        })
                  }

                })
          })
        });
    });
    setTimeout(triggerFunction, duration);
  }

What I want is first to receive the result from the function

var alreadyExist = checkAlreadyElementDB(firstData.appType, doc.id)

Then to continue to check the following part of the code

      if(firstData.open == true && !alreadyExist){
                      console.log("Entered here: " + alreadyExist)
                      db.collection('#name').doc(newID).set({
                        #NotImportant
                      })
                      db.collection('users').doc(doc.id).collection('#aName').doc(doc1.id).update({
                        open: false
                      })
      } else if(firstData.open == true && alreadyExist){
                        db.collection('users').doc(doc.id).collection('#aName').doc(doc1.id).update({
                          open: false
                        })
                  }

I will really appreciate your help with this problem!

checkAlreadyElementDB() function looks like this:

  function checkAlreadyElementDB(appType, userID){
      var db = firebase.firestore();
      db.collection('#name').get().then((querySnap) => {
        querySnap.forEach((doc) => {
          var opFCData = doc.data();
          if(opFCData.appType == appType && opFCData.userID == userID){
          //  console.log("I HAVE ENTERED HERE")
            return true
          }
        });
      });
      return false
  }
3
  • since db.collection('#name').get() is asynchronous, you need to return a Promise from checkAlreadyElementDB, and where you use checkAlreadyElementDB you need to use .then (or aysnc/await) to wait for that promise to resolve Commented Mar 25, 2020 at 0:23
  • you're already using them in your code db.collection('#name').get() returns a Promise ... as you can see by the .then( .... ) ... anywhere you've used .then in your code, you're using promises Commented Mar 25, 2020 at 0:29
  • I see, still not sure how to make it work to allow first to execute that function, then to continue with if else... if you have time, I would appreciate a code example so I can see Commented Mar 25, 2020 at 1:24

1 Answer 1

1

The key is to break this logic up into smaller, testable, promise-returning functions. Let's start with checkAlreadyElementDB

// all the functions need this, move it to the top of the file
const db = firebase.firestore();

function checkAlreadyElementDB(appType, userID) {
  return db.collection('#name')
    .where('appType', '==', appType)
    .where('userID', '==', userID).get().then(snapshot => {
      return !snapshot.empty
  })
}

Notice how the check can be made using where clauses on the query. This is a much quicker operation, transferring much less data to the client. Notice also how this and all of the other functions return promises

Next, the center of the OP code took a little effort to untangle. There were nested promises and names that made it tough to follow. I did my best here to understand what the code meant, despite not really understanding the app.

function closeThenSetOrUpdate(doc) {
  let data = doc.data()
  // update the passed doc, no matter what the checkAlready... result is
  return doc.ref.update({ open: false }).then(() => {
    return checkAlreadyElementDB(data.appType, doc.id)
  }).then(exists => {
    if (data.open && !exists) {
      console.log("Entered here: " + exists)
      const newID = createRandomId()
      return db.collection('#name').doc(newID).set({ #NotImportant })
    } else {
      return Promise.resolve() // just an empty promise
    }
  })
}

Here's a function that handles each user by invoking the logic above. Notice that it gathers promises and executes them all together with Promise.all()

function updateANamesForUserWithID(userId) {
  return db.collection('users').doc(userId).collection('#aName').get().then(snapshot => {
    let promises = snapshot.docs.map(doc => {
        return closeThenSetOrUpdate(doc)
    })
    return Promise.all(promises)
  })
}

Now, the core function is simpler to write, using the same Promise.all() technique...

function triggerFunction(duration) {
  return db.collection('users').get().then(snapshot => {
    let promises = snapshot.docs.map(user => {
      return updateANamesForUserWithID(user.id)
    })
    return Promise.all(promises)
  }).then(() => {
    setTimeout(triggerFunction, duration);
  })
}
Sign up to request clarification or add additional context in comments.

3 Comments

One thing which I observed is that here return db.collection('users').doc(doc.id).collection('#aName').get().then(snapshot => { I don't see the declaration of doc.id. Is it supposed to be declared before in the triggerFunction or should I have another one and pass it as a param into triggerFunction?
@ForBugged - see edit. Same technique applied to each user. Caveat is that I can't guarantee that this code even compiles, since it's a big job (as you know to build a rig to test it). Also note, that I'm flying blind on the meanings of the entities and logic (making it harder to keep track). There's a whole other topic to discuss here about naming things in a way that helps future readers (including yourself) makes sense of the code.
thank you for your answer! Now I see how I can go ahead with this. I solved my problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.