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“一次”事件不返回承诺

从firebase onchange返回observable

@angular/fire AngularFireStorage getDownloadUrl()

Angular 2 Firebase Observable 承诺不会返回任何东西