我有一个用于网站的简单且简短的javascript代码:
db.collection("classes").doc(data.readGrades).get().then(function(doc) {
if (doc.exists) {
const data = doc.data();
const members = data.members;
members.forEach(el => {
db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
const data = doc.data();
//some stuff
});
});
})
} else {
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
如您所见,我需要运行两个Firebase Firestore任务。重要的是,此操作首先运行db.collection("classes").doc(data.readGrades).get().then(function(doc) {
,并且对于每个成员,此任务都遵循db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) {
(注意:由于变量el
,第二个任务需要第一个任务)。所以我的问题是我必须等待完成第二个任务,然后才能继续下一个成员的循环。
这正是我的问题:在为下一个成员运行循环之前如何完成第二个任务?
编辑:
我这样修改了Javascript代码:
db.collection("classes").doc(data.readGrades).get().then(function(doc) {
if (doc.exists) {
const data = doc.data();
const members = data.members;
members.reduce((chain, el) => {
table_number++;
const html = fillTemplate("grade_table" + table_number, member_name);
document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);
return chain.then(() =>
db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
const data = doc.data();
addToTable("grade_table" + table_number, doc.id, data.mdl, data.klu);
});
})
)
}, Promise.resolve());
} else {
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
function fillTemplate(table_name, id) {
console.log("ID OF TABLES " + table_name);
return `
<div class="noten_tabelle_permission" id="noten_tabelle_permission">
<h1 id="member_name">${id}</h1>
<table id="${table_name}" style="width:100%">
<tr>
<th>Fach</th>
<th>mündlich</th>
<th>Klausur</th>
</tr>
<!-- Make content with js code -->
</table>
</div>
`;
}
function addToTable(table_name, subject, mdl, klu) {
var subject_name = getSubjectByNumber(subject);
var short_subject = getSubjectShortByNumber(subject);
//Zeile erstellen
console.log("addToTable " + table_name);
var y = document.createElement([short_subject]);
y.setAttribute("id", [short_subject]);
document.getElementById([table_name]).appendChild(y);
//Spalten in einer Zeile
var y = document.createElement("TR");
y.setAttribute("id", [short_subject]);
//Spalten in einer Zeile
var cE = document.createElement("TD");
var tE = document.createTextNode([subject_name]);
cE.appendChild(tE);
y.appendChild(cE);
var a = document.createElement("TD");
var b = document.createTextNode([mdl]);
a.appendChild(b);
y.appendChild(a);
var c = document.createElement("TD");
var d = document.createTextNode([klu]);
c.appendChild(d);
y.appendChild(c);
document.getElementById(table_name).appendChild(y);
}
所以诺言行得通。但是现在我有一个问题,就是诺言行不通。如您所见,我想为每个成员运行一个Firestore(Database)任务。因此,诺言应解雇每个新成员。但是,诺言最终会首先兑现。
背景知识:我从数据库的ArrayList中获取成员,对于每个单个成员,我都需要基于该成员获取特定值。加载成员的值后,我将它们添加到表中,该表将填充上一个任务中的每个成员(如您所见)。正如我所说的,诺言最终将兑现。 因此,值不会添加到成员的特定表中。它们将被添加到最后一个表中。
编辑2 : 这是编辑后的代码:
members.reduce((chain, el) => {
db.collection("users").doc(el).collection("user_data").doc("u").get().then(function (doc) {
const data = doc.data();
var member_name = data.name;
table_number++;
const html = fillTemplate("grade_table" + table_number, member_name);
document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);
})
return chain.then(((table_num) => {
return db.collection("users").doc(el).collection("grades").get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
const data = doc.data();
addToTable("grade_table" + table_num, doc.id, data.mdl, data.klu);
});
})
}).bind(null, table_number))
}, Promise.resolve());
所以这里的问题是,我获得member_name的任务异步运行,而另一部分则不然。因此,只有在加载了所有内容后,我才能运行整个任务。因为如您所见,我还需要member_name。因此,如何重新编码它,以便仅在加载所有内容(member_name / grades)时任务才能运行。
答案 0 :(得分:1)
您可以使用reduce
代替forEach
,并使用空Promise
作为初始值,以便顺序处理成员:
db.collection("classes").doc(data.readGrades).get().then(function(doc) {
if (doc.exists) {
const data = doc.data();
const members = data.members;
members.reduce((chain, el) => {
return chain.then(() =>
db.collection("users").doc(el).collection("grades").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
const data = doc.data();
//some stuff
});
})
)
}, Promise.resolve());
} else {
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
更新#1 :
考虑到更新的问题,数据可能会添加到最后一个表中,因为在答应解决时所有的reduce迭代都已完成(因此,table_name
变量保留了上次迭代的值)。您可以通过对承诺解决方案调用的回调的binding
table_name
值来解决该问题:
members.reduce((chain, el) => {
table_number++;
const html = fillTemplate("grade_table" + table_number, doc.id);
document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);
return chain.then(((table_num) => {
return db.collection("users").doc(el).collection("grades").get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
const data = doc.data();
addToTable("grade_table" + table_num, doc.id, data.mdl, data.klu);
});
})
}).bind(null, table_number))
}, Promise.resolve());
更新#2 :
为了随后在单个reduce迭代中运行多个异步操作,请尝试使用Promise的then
方法将它们链接起来:
members.reduce((chain, el) => {
return chain
.then(() => db.collection("users").doc(el).collection("user_data").doc("u").get())
.then(doc => {
const data = doc.data();
var member_name = data.name;
table_number++;
const html = fillTemplate("grade_table" + table_number, member_name);
document.getElementById("main_padding").insertAdjacentHTML('beforeend', html);
return table_number;
})
.then(table_num => {
return db.collection("users").doc(el).collection("grades").get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
const data = doc.data();
addToTable("grade_table" + table_num, doc.id, data.mdl, data.klu);
});
})
})
}, Promise.resolve());
答案 1 :(得分:1)
另一种方法是按如下方式使用Promise.all()
:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
由db.collection("classes").doc(data.readGrades).get()
.then(doc => {
if (doc.exists) {
const data = doc.data();
const members = data.members;
const promises = []
members.forEach(el => {
const usersPromise = db.collection("users").doc(el).collection("grades").get();
promises.push(usersPromise);
});
return Promise.all(promises);
} else {
console.log("No such document!");
throw new Error("No such document!");
}
})
.then(results => {
results.forEach(querySnapshot => {
querySnapshot.forEach(doc => {
const data = doc.data();
//some stuff
});
});
})
.catch(error => {
console.log("Error", error);
});
返回的单个Promise解析时返回的results
是一个包含所有承诺结果的数组,的顺序与您在{{1}中推送承诺的顺序相同}数组。