0

The code below scans all users looking for nodes to be archived: function archiveColab()

The problem is that the function is inside a forEach that scans the /users/ node and then needs to scan the /collaborative child, so it has another forEach inside.

When I run the arquivaColab function it overwrites the daily records in the monthly without counting the total of each cycle, leaving only the last cycle for that month.

This is due to asynchronous execution forEach and I'm not able to find a way to sequentially read and write these records.

When adding await/promisse, apparently the cycle ends after executing the first cycle/record.

I understand that this is a problem with understanding how asynchronous mode works and I have been trying to find a solution for a few days.

Any help would be very welcome.

CODE:

exports.todoDia = functions.runWith({memory: '1GB', timeoutSeconds: '300'}).pubsub.schedule('10 15 * * *').timeZone('America/Sao_Paulo').onRun( async () => {
    
    const cutoff = format(new Date().setDate(new Date().getDate() - 7), 'Y-m-d'); // 7 days ago
    var userRef = admin.database().ref("users");
    return userRef.once('value').then((snapshot) => {
        snapshot.forEach(user => {
            var userID = user.key;
            if (userID === "F54jCdsPt8ZLIF17dYn8U0knMMY2") { // Only this user for testing
                console.log(userID + " " + cutoff);
                user.child('collaborative').forEach(async collaborative => {
                    if (collaborative.key <= cutoff) { await arquivaColab(userID, collaborative.key, collaborative.val()); }
                });
            }
        });
        return null;
    });
});

async function arquivaColab(userID, keyColab, valColab) {
    console.log("userID: "+userID+" | keyColab: "+keyColab+" | valColab: "+valColab);
    const ref = admin.database().ref("/collaborative/"+userID+"/"+keyColab.substring(0,7));
    var stringDest = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
    return ref.once('value').then((snapDest) => {
        if (snapDest.exists()) { stringDest = snapDest.val() }

        var wordsOrig = valColab.split(" ");        
        var wordsDest = stringDest.split(" ");
    
        var strFinal = "";
        for (var i = 0; i < wordsOrig.length; i += 1) {    
            strFinal += (Number(wordsOrig[i]) + Number(wordsDest[i])) + " ";
        }
    
        console.log("DESTINO: /collaborative/"+userID+"/"+keyColab.substring(0,7)+" : "+strFinal);
        return admin.database().ref("/collaborative/"+userID+"/"+keyColab.substring(0,7)).set(strFinal.trim());
        // console.log("ORIGEM : /users/"+userID+"/collaborative/"+keyColab+" : "+valColab);
        // admin.database().ref("/users/"+userID+"/collaborative/"+keyColab.set(null);

    });
}

JSON:

  "users" : {
    "F54jCdsPt8ZLIF17dYn8U0knMMY2" : {
      "collaborative" : {
        "2021-03-12" : "0 0 0 0 0 0 1 0 0 2 0 0 0 0 0 0 0 0 0 0",
        "2021-03-15" : "0 0 0 0 0 0 1 0 0 2 0 0 0 0 0 0 0 0 0 0",
        "2021-07-07" : "0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0"
      },
      "username" : "John Doe"
    },
    "Z5EEkzyG2WPlCHa9a6gDpBnIr2u2" : {
      "collaborative" : {
        "2021-07-06" : "0 0 0 0 0 0 1 0 0 2 0 0 0 0 0 0 0 0 0 0",
        "2021-07-07" : "0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0"
      },
      "username" : "John Next"
    },
  },

To understand the desired functioning: Each user has a node called "collaborative"

YYYY-MM-DD : "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"

There are 20 counters that record different activities of each user on a daily basis. After 7 days Firebase Functions must archive these records counting the sum of each counter in a monthly record in /collaborative/userID/.

1 Answer 1

1

forEach does not support async/await. You would need to store the data into an array and use a for loop that supports async/await like here:

return userRef.once("value").then(async (snapshot) => {
  const tempArray = [];

  if (userID === "F54jCdsPt8ZLIF17dYn8U0knMMY2") {
    // Only this user for testing
    console.log(userID + " " + cutoff);
    user.child("collaborative").forEach(async (collaborative) => {
      if (collaborative.key <= cutoff) {
        tempArray.push({
          userID,
          key: collaborative.key,
          val: collaborative.val(),
        });
      }
    });
  }

  for (let i = 0; i < tempArray.length; i++) {
    const { userID, key, val } = tempArray[i];
    await arquivaColab(userID, key, val);
  }
});

return null;

Sign up to request clarification or add additional context in comments.

1 Comment

IT WORKED! I had to make some adjustments to the logic, but the suggested concept of storing the data in an array was exactly what I needed. Thank you very much!

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.