Javascript for 循环不等待 .then 完成
Posted
技术标签:
【中文标题】Javascript for 循环不等待 .then 完成【英文标题】:Javascript for loop not waiting for .then to finish 【发布时间】: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 可能会有所帮助。 【参考方案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<Promise[]>
,而不是Promise[]
本身,现在已经更正了。如果你制作loadUserProfileKeys
async
,可以进一步简化。
感谢您修复 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];
【讨论】:
以上是关于Javascript for 循环不等待 .then 完成的主要内容,如果未能解决你的问题,请参考以下文章
使用javascript的Symbol.asyncIterator来等待循环