Javascript for 循环不等待 .then 完成

Javascript for loop not waiting for .then to finish

我有以下代码从 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]);
  }
}

日志记录本身很好 - 它不会记录“循环结束”,直到完成所有循环,即多个“循环开始”日志。 问题是这仍然是 before keyResults.push(value); 被调用 - 因此数组是空的(或者偶尔只有部分填充)。

如何让 var downloadURL = pathRef.getDownloadURL()getValueKey(url, key) 阻塞 - 以便在调用嵌套的 .then((value) 以添加到数组之前它不会遍历循环?

我无法理解异步等 - 我不断收到语法错误。


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

  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);
}

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]);
  }
}

如果你想等待多个异步操作全部完成,你会想要使用Promise.all()

类似这样的事情应该会越来越近:

return initialUserProfilesRef.listAll()
  .then((res) => {
    return Promise.all(
      res.items.map((itemRef) => { // Loop over each item
        // Ignore profiles with symbols (workaround - TODO: fix this)
        if (/,|&/.test(itemRef.name)) {
          // you might want to return something here, e.g.
          return { skipped: true }
        } 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)
            });
        }
      })
    );
  })
  .then((keyResults) => {
    console.log("End for loop");
    console.log(keyResults);
  })

我会让 loadUserProfileKeys 成为一个异步函数。

这样你就可以简单地等待你的其他异步函数(pathRef.getDownloadURL()getValueKey(url, key))。

这是使用 async & await 修改的 Snipped。 我没有测试它,但它应该可以工作。

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);
  }
}

总的来说,我个人建议尽量避免封装 .then() 调用。

这只会让代码更难阅读和理解。

我发现 async & await 更简洁。

你可以这样做

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]);
    }
}

所以补充一下 - 这按预期工作但可能不是最佳 + 可能需要整理一下:

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

  // Results array
  var keyResults = [];

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

  var promises = [];

  // Loop over each file
  for (const itemRef of profiles.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);

      // Add to the array of Promises
      promises.push(doSomething(pathRef, key));
    };
  };

  // Wait for all Promises to resolve
  keyResults = await Promise.all(promises)

  console.log("End for loop");
  console.log(keyResults);
}

async function doSomething(pathRef, key) {
  var downloadURL = await pathRef.getDownloadURL();
  var value = await getValueKey(downloadURL, key);
  return value
}

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 results[0];
  }
}