firebase 交易,您必须返回一个承诺
Posted
技术标签:
【中文标题】firebase 交易,您必须返回一个承诺【英文标题】:firebase transactions you must return a promise 【发布时间】:2019-03-04 15:01:06 【问题描述】:我试图弄清楚如何从 Angular 6 / typescript 应用程序将事务写入 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 transactions = [];
2) 开始事务:return db.runTransactions(function(transaction) …
3) 使用事务执行数据库查询:transaction.set(newPhraseRef,phraseItem);
4) 将查询返回的交易推送到交易数组:transactions.push(transaction.set(newPhraseRef,phraseItem));
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()-callback 中返回一个 Promise。
但我正在回报一个承诺,不是吗?这行:return Promise.all(transArray) 是我要返回的承诺。没有?
【问题讨论】:
在您发布的示例中,transactions[]
用于从forEach
循环中收集承诺。这就是他们使用Promise.all
的原因。由于您只执行一项操作,因此您应该能够在没有transArray[]
的情况下使用return t.set(docRef, acceptance)
。但是,这不应该解决您的问题。
【参考方案1】:
事实证明,这是最好的方法:
return t.get(pendingDocRef).then(data =>
t.set(pendingDocRef, acceptance, merge:true);
);
get(...) 返回它所要求的承诺,因此 set(...) 不必这样做。
我不喜欢这个。我仍然认为我应该能够自己在事务中进行集合(即没有获取)。
我还发现这种 可以工作:
return t.set(...).commit();
commit() 也返回了一个承诺,但是我得到了非常奇怪的行为:事务一遍又一遍地重复自己,直到它进入 catch 块并出现“事务已过期”的错误。尽管如此,数据会被提交到数据库,所以如果你不介意错误(让它通过),commit() 也可以。
详情见此贴:firebase transaction repeating when committing
【讨论】:
我也遇到了同样的问题,我认为这是有道理的。在写入之前,您应该在事务中加载文档的当前状态。只有这样,交易才真正有意义,因为如果您不这样做,文档可能会同时发生变化。【参考方案2】:请阅读Transaction set()
的文档 - 它不会返回承诺。这可能会使 Firestore SDK 感到困惑,因为您返回的 Promise.all()
包含一系列不是承诺的东西。所以它可能认为你返回了一个错误。
您可能根本不需要从事务处理函数返回任何内容。只需致电set()
即可完成。如果您想通过它返回的承诺pass that value out to the caller of runTransaction,您只需要从您的事务处理程序返回一些东西。
顺便说一句,在您的第一个示例中,您不应该将 Promise 收集到在处理函数之外定义的数组中。如果事务处理程序运行不止一次,这可能会导致问题。正如我链接到的文档所说:
请勿在事务函数中修改应用程序状态。 这样做会引入并发问题,因为事务 函数可以运行多次,不保证在 界面线程。
事务之外的数组将被视为“应用程序状态”,因为它不在函数本身内。
【讨论】:
感谢您的回复,道格。我取出事务数组并返回承诺,使其看起来像这样: return db.runTransaction(function(t) const docRef = db.collection('Pending').doc ( 感谢您的回复,道格。我取出事务数组并返回承诺,使其看起来像这样: return db.runTransaction(function(t) const docRef = db.collection('Pending').doc(req.query.inviteId); t。 set(docRef, 接受); ).then(result => console.log('result = ', result); ).catch(err => console.log('err = ', err); ); ); ...但我仍然遇到同样的错误。我还尝试返回 t.set(docRef, 接受)。我还尝试在 t.set(docRef, 接受) 之后返回一个承诺。我仍然得到同样的错误。这就像 db.runTransaction(...) 不能与 then 链接。以上是关于firebase 交易,您必须返回一个承诺的主要内容,如果未能解决你的问题,请参考以下文章
Cloud Functions for Firebase onWrite 超时
Firebase 函数 tslint 错误 必须正确处理承诺
从firebase onchange返回observable