Home > other >  Javascript for loop not waiting for .then to finish
Javascript for loop not waiting for .then to finish

Time:11-26

I have the following code to retrieve some file information from Firebase:

function loadUserProfileKeys(key) {
  // Get the Firebase storage ref for the InitialUserProfiles folder
  var storageRef = firebase.storage().ref();
  var initialUserProfilesRef = storageRef.child('InitialUserProfiles'); // .txt files folder

  // Array
  const keyResults = [];

  // Retrieve all profiles
  initialUserProfilesRef.listAll()
    .then(function(res) {

      // Loop over each item
      for (const itemRef of res.items) {
        console.log("Start for loop");

        // Ignore profiles with symbols (workaround - TODO: fix this)
        if (/,|&/.test(itemRef.name)) {} else {
          // Path of the file
          var pathRef = initialUserProfilesRef.child(itemRef.name);

          // Get the file's download URL
          var downloadURL = pathRef.getDownloadURL()
            .then((url) => {

              // Get the given key from the user profile text file
              getValueKey(url, key)
                .then((value) => {
                  // Add it to the keyResults array
                  keyResults.push(value);
                });
            });
        }
      }
      console.log("End for loop");
      console.log(keyResults);
    }).catch((error) => {
      console.log("ERROR");
      console.log(error);
    });
}

async function getValueKey(fileURL, key) {
  let response = await fetch(fileURL);

  if (response.status == 200) {
    let json = await response.text(); // (3)
    var lines = json.split("\n");
    var results = [];
    for (var i = 0; i < lines.length; i  ) {
      var line = lines[i];
      var pairs = line.split(":");
      if (pairs.length == 2 && pairs[0].trim() == key) {
        results.push(pairs[1].trim());
      }
    }
    return Promise.resolve(results[0]);
  }
}

The logging itself is fine - it won't log "End for loop" until all of the looping, i.e. multiple "Start for loop" logs, is done. The issue is that this is still before keyResults.push(value); is called - and thus the array is empty (or very occasionally only partly populated).

How do I get var downloadURL = pathRef.getDownloadURL() and getValueKey(url, key) to be blocking - so that it does not iterate over the loop until the nested .then((value) is called to add to the array?

I can't figure out async etc - I keep getting syntax errors.

CodePudding user response:

If you want to wait for multiple asynchronous operations to all be finished, you'll want to use Promise.all().

Something like this should be getting a lot closer:

const keyResults = Promise.all(initialUserProfilesRef.listAll().then(function(res) {
  // Loop over each item
  return res.items.map((itemRef) => {
    // Ignore profiles with symbols (workaround - TODO: fix this)
    if (/,|&/.test(itemRef.name)) {} else {
      // Path of the file
      var pathRef = initialUserProfilesRef.child(itemRef.name);

      // Get the file's download URL
      return pathRef.getDownloadURL()
        .then((url) => {
          // Get the given key from the user profile text file
          return getValueKey(url, key)
        });
    }
  }
})
console.log("End for loop");
console.log(keyResults);

CodePudding user response:

I would make loadUserProfileKeys a async function.

That way you can simply await your other async functions (pathRef.getDownloadURL(), getValueKey(url, key)).

Here is your Snipped modified using async & await. I did not test it, but it should work.

async function loadUserProfileKeys(key) {
  // Get the Firebase storage ref for the InitialUserProfiles folder
  var storageRef = firebase.storage().ref();
  var initialUserProfilesRef = storageRef.child('InitialUserProfiles'); // .txt files folder

  // Array
  const keyResults = [];

  try {
    // Retrieve all profiles
    const profileRes = await initialUserProfilesRef.listAll();

    // Loop over each item
    for (const itemRef of profileRes.items) {
      console.log("Start for loop");

      // Ignore profiles with symbols (workaround - TODO: fix this)
      if (/,|&/.test(itemRef.name)) {} else {
        // Path of the file
        var pathRef = initialUserProfilesRef.child(itemRef.name);

        // Get the file's download URL
        var downloadURL = await pathRef.getDownloadURL();

        // Get the given key from the user profile text file
        keyResults.push(await getValueKey(downloadURL, key));
      }
    }
    console.log("End for loop");
    console.log(keyResults);
  } catch(error) {
    console.log("ERROR");
    console.log(error);
  }
}

In general i would personally recommend try to avoid encapsulating .then() calls.

It just makes the code harder to read and understand.

I find async & await much cleaner.

CodePudding user response:

You can do like this

async function loadUserProfileKeys(key) {
    // Get the Firebase storage ref for the InitialUserProfiles folder
    var storageRef = firebase.storage().ref();
    var initialUserProfilesRef = storageRef.child('InitialUserProfiles'); // .txt files folder

    // Array
    const keyResults = [];

    // Retrieve all profiles
    const res = await initialUserProfilesRef.listAll();

    // Loop over each items
    res.items.forEach(async (itemRef) => {
        console.log("Start for loop");

        // Ignore profiles with symbols (workaround - TODO: fix this)
        if (/,|&/.test(itemRef.name)) {
            // skip
        } else {
            // Path of the file
            const pathRef = initialUserProfilesRef.child(itemRef.name);

            // Get the file's download URL
            const url = await pathRef.getDownloadURL();
            
            // Get the given key from the user profile text file
            const value = await getValueKey(url, key);
            .
            // Add it to the keyResults array
            keyResults.push(value);
        }
    });
    console.log("End for loop");
    console.log(keyResults);
}

async function getValueKey(fileURL, key) {
    let response = await fetch(fileURL);

    if (response.status == 200) {
        let json = await response.text(); // (3)
        var lines = json.split("\n");
        var results = [];
        for (var i = 0; i < lines.length; i  ) {
            var line = lines[i];
            var pairs = line.split(":");
            if (pairs.length == 2 && pairs[0].trim() == key) {
                results.push(pairs[1].trim());
            }
        }
        return Promise.resolve(results[0]);
    }
}
  • Related