【问题标题】:Javascript for loop not waiting for .then to finishJavascript for 循环不等待 .then 完成
【发布时间】:2022-01-04 01:27:24
【问题描述】:

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

日志本身很好 - 在所有循环(即多个“开始循环”日志)完成之前,它不会记录“循环结束”。 问题是这仍然是 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 阻塞。你可能不得不使用回调。
  • 我对 firebase 一无所知,但async function 可能会有所帮助。

标签: javascript firebase firebase-storage


【解决方案1】:

如果您想等待多个异步操作全部完成,您需要使用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);
  })

【讨论】:

  • 感谢您的帮助 :) 我已尝试使用您的建议来编辑问题。我现在收到Uncaught (in promise) TypeError: Argument of Promise.all is not iterable
  • @SwiftBehemoth 这种方法最初将Promise.all() 交给Promise&lt;Promise[]&gt;,而不是Promise[] 本身,现在已经更正了。如果你制作loadUserProfileKeysasync,可以进一步简化。
  • 感谢您修复 Sam! ? 我已经盯着它看了一段时间,但没有看到我的错误。
  • 非常感谢你们! :)
【解决方案2】:

我会将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 更干净。

【讨论】:

  • 警告:虽然这种方法可行,但此答案会逐项处理每个项目。随着getDownloadURL() 解决问题的时间,应该使用并行方法。
  • @samthecodingman 这是真的。作为额外的优化,您可以用对项目的 .map() 调用替换 for 循环。那些 map 调用应该返回一个 Promise。并使用 Promise.all() 并行处理它们
【解决方案3】:

你可以这样做

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

【讨论】:

  • 警告:正如所写,此答案没有正确等待每个 Promise 在返回结果之前解决,因此不回答问题。
  • @samthecodingman 你确定吗?我不这么认为......它将等待每个承诺解决。在直接说它不起作用之前,您应该提供它会失败的原因。
【解决方案4】:

所以补充一下 - 这可以按预期工作,但可能不是最佳的 + 可能会稍微整理一下:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-09
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多