我正在尝试弄清楚如何从angular 6 /打字稿应用程序将事务写入Firebase数据库。
我用作指导的一个工作示例如下:
const transactions = [];
return db.runTransaction(function(transaction) {
// const promises = [];
descriptionsInDB.forEach(dbDescription => {
if (dbDescription.matched === false) {
// item wasn't found, so add the item to the transaction.
const phraseItem: PhrasesDB = {
query: dbDescription.description,
updatedAt: Date.now(),
createdAt: Date.now(),
workflows: {
[workflowId]: true
}
};
const newPhrasesRef = phrasesCol.doc();
transactions.push(transaction.set(newPhrasesRef, phraseItem));
} else {
if (dbDescription.data.workflows && dbDescription.data.workflows[workflowId]) {
// do nothing, this phrase is already part of the record.
} else { // update the workflows that are part of the record.
const workflows = {
workflows: {
[workflowId]: true
}
};
const phrasesRef = phrasesCol.doc(dbDescription.dbId);
transactions.push(transaction.update(phrasesRef, workflows));
}
}
});
return Promise.all(transactions);
})
我确定的关键要素如下:
1)创建一个数组来保存您的交易:const transaction = [];
2)开始事务:返回db.runTransactions(function(transaction){…
3)使用以下事务执行数据库查询:transaction.set(newPhraseRef,statementItem);
4)将查询返回的事务推送到事务数组:transactions.push(transaction.set(newPhraseRef,statementItem));
5)用交易数组返回一个承诺:return Promise.all(transaction);
如果我拥有这项权利,那么我应该可以将此公式应用于我要编写的交易,并且应该可以运行:
const pendingRef = `Pending/${req.query.inviteId}`;
const acceptance = {
'cryptoInvitationAcceptance': req.body.cryptoInvitationAcceptance,
'reason': (req.body.reason !== undefined ? req.body.reason : '')
}
return db.runTransaction(function(t) {
const transArray = [];
const docRef = db.collection('Pending').doc(req.query.inviteId);
transArray.push(t.set(docRef, acceptance));
return Promise.all(transArray);
}).then(result => {
console.log('result = ', result);
}).catch(err => {
console.log('err = ', err);
});
但是它总是转到catch块并打印出消息:
err =错误:您必须在transaction()回调中返回Promise。
但是我正在兑现诺言,不是吗?这行代码:return Promise.all(transArray)是我要返回的承诺。不?
答案 0 :(得分:0)
请阅读Transaction set()
的文档-它不返回承诺。这可能会使Firestore SDK感到困惑,因为您将返回Promise.all()
一系列不是承诺的东西。因此,也许它认为您返回了错误。
您可能根本不需要从事务处理程序函数中返回任何内容。只需调用set()
即可完成。如果您想通过返回的承诺pass that value out to the caller of runTransaction,只需从事务处理程序中返回一些东西。
顺便说一句,在您的第一个示例中,您不应该将promise收集到在处理函数之外定义的数组中。如果事务处理程序多次运行,可能会导致问题。正如我链接的文档所述:
请勿在事务功能内部修改应用程序状态。 这样做会引入并发问题,因为交易 函数可以运行多次,并且不能保证在 用户界面线程。
事务外的数组将被视为“应用程序状态”,因为它不在函数本身内部。
答案 1 :(得分:0)
事实证明,这是最好的方法:
return t.get(pendingDocRef).then(data => {
t.set(pendingDocRef, acceptance, {merge:true});
});
get(...)返回所要的承诺,因此set(...)不必这样做。
我不喜欢这样。我仍然认为我应该能够自行完成交易中的交易(即无交易)。
我还发现,这类 有用:
return t.set(...).commit();
commit()还返回一个promise,但是然后我得到了一个奇怪的行为:事务一遍又一遍地重复,直到它进入catch块,并出现“事务已过期”错误。尽管如此,数据仍会提交到数据库,因此,如果您不介意错误(只是让它通过),则commit()也会起作用。
有关详情,请参见此帖子:firebase transaction repeating when committing