我需要遍历从API调用返回的数据并将其插入到Firebase中。为了防止Firebase API过载和超时,我正在使用这个"链"确保前一个循环及其所有承诺在进入下一个循环之前完成的技术。来自Firebase的读取/获取触发并返回预期结果。
写入/设置为Firebase触发器,我知道这是因为" console.log(父母)"执行并显示正确的数据,然后"然后"在" set"之后打印"添加ID"的学生。但是,没有数据写入云中的数据存储区,也不会显示错误。如果我自己运行write / set,在循环和链之外,它运行正常。
function writeDataToFirestoreStudents(data) {
let chain = Promise.resolve();
for (let i = 0; i < data.length; ++i) {
var parents = [];
chain = chain.then(()=> {
db.collection("parents").where("student_id", "==", data[i].kp_ID)
.get()
.then(function(querySnapshot) {
parents = [];
querySnapshot.forEach(function(doc) {
//console.log(doc.id, " => ", doc.data());
parents.push(doc.data().bb_first_name + " " + doc.data().bb_last_name);
});
return parents;
}).then(function(parents) {
console.log(parents);
var docRef = db.collection('students').doc(data[i].kp_ID);
var setAda = docRef.set({
bb_first_name: data[i].bb_first_name,
bb_last_name: data[i].bb_last_name,
bb_current_grade: data[i].bb_current_grade,
bb_class_of: data[i].bb_class_of,
parents: parents,
}).then(ref => {
console.log('Added student with ID: ', data[i].kp_ID);
});
}).catch(function(error) {
console.error("Error writing document: ", error);
});
})
.then(Wait)
}
}
function Wait() {
return new Promise(r => setTimeout(r, 25))
}
答案 0 :(得分:1)
.batch
。这对我来说很好。
pub.addToFirestore = function(){
var countries = [
{"name": "Afghanistan", "code": "AF"},
{"name": "Åland Islands", "code": "AX"},
{"name": "Albania", "code": "AL"},
{"name": "Algeria", "code": "DZ"},
{"name": "American Samoa", "code": "AS"},
{"name": "AndorrA", "code": "AD"},
{"name": "Angola", "code": "AO"},
{"name": "Anguilla", "code": "AI"},
{"name": "Antarctica", "code": "AQ"},
{"name": "Antigua and Barbuda", "code": "AG"},
{"name": "Zimbabwe", "code": "ZW"}
];
var db = firebase.firestore();
var batch = db.batch();
for (var i = 0; i < countries.length; i++) {
var newRef = db.collection("countries").doc(countries[i].code);
batch.set(newRef, {name: countries[i].name});
}
//Commit the batch
batch.commit().then(function () {
console.log("done");
});
};
答案 1 :(得分:1)
您遇到的主要问题是您尝试将同步和异步元素混合在一起。 您应该真正隔离异步部分的所有必需变量。
我为“长答案”
道歉这方面的一个例子是:(来源:https://codeburst.io/asynchronous-code-inside-an-array-loop-c5d704006c99)
var output = '';
var array = ['h','e','l','l','o'];
// This is an example of an async function, like an AJAX call
var fetchData = function () {
return new Promise(function (resolve, reject) {
resolve();
});
};
/*
Inside the loop below, we execute an async function and create
a string from each letter in the callback.
- Expected output: hello
- Actual output: undefinedundefinedundefinedundefined
*/
for (var i = 0; i < array.length; i++) {
fetchData(array[i]).then(function () {
output += array[i];
});
}
有几种方法可以解决这个问题,你可以用一个函数包装for循环的内部,例如:
for (var i = 0; i < array.length; i++) {
(function (letter) {
fetchData(letter).then(function () {
output += letter;
});
})(array[i])
}
但是,这样做的缺点是没有办法知道调用何时完成(没有回调怪物),并且错误处理变得......很难...... 有趣的是,JavaScript现在已经有了内置的工具。意思是你可以做这样的事情:
array.forEach(letter => {
fetchData(letter)
.then(function () {
output += letter;
})
})
但是,仍然存在不知道什么时候一切都完成的问题。
这是承诺非常有用的地方。
我们假设您有两个功能需要不同的时间来完成。
在继续之前,您可以使用Promise.all
等待他们解决问题。
const foo500 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'foo500');
});
const bar800 = new Promise((resolve, reject) => {
setTimeout(resolve, 800, 'bar800');
});
const both = Promise.all([foo500, bar800])
.then(function(values) {
// Logs: Array ['foo500', 'bar800']
console.log(values);
});
在800毫秒后的这个例子中,所有&#39;将解决从两个承诺中提供一系列值。
还有一件要做的事情就是&#39; forEach&#39;不会从它的功能输出值,这是&#39; map&#39;介入。
这意味着我们可以轻松地输出或承诺数组Promise.all
等待&#39;等等。对
这最终促使我有希望帮助。
function writeDataToFirestoreStudents(data) {
// Allow the output to know when we're done.
return Promise.all(
// Here we build our promises
data.map(student =>
// Do the first database query
db.collection('parents')
.where('student_id', '==', student.kp_ID)
.get()
// Convert the results into an array of parents.
.then(snapshot => snapshot.map(doc =>
doc.data().bb_first_name + " " +
doc.data().bb_last_name
))
// Use the 'parent' array.
.then(parents =>
// Perform the next database query.
db.collection('students')
.doc(student.kp_ID)
.set({
// Note here 'student' has not changed and is in scope.
bb_first_name: student.bb_first_name,
bb_last_name: student.bb_last_name,
bb_current_grade: student.bb_current_grade,
bb_class_of: student.bb_class_of,
// ES6 short hand.
parents
})
)
// Spread your error net
.catch(err => console.error(
"Error writing document: ", error
))
)
)
}
// You could use the function like so
writeDataToFirestoreStudents(data)
// You can now use 'then' to tell when we're done.
.then(students => {
// This is the array of students
console.log(students);
})
重要的是要注意我没有测试过这段代码,所以我不能保证它会第一次运行。