【问题标题】:Firebase Functions read and add data in same onCall functionFirebase 函数在同一个 onCall 函数中读取和添加数据
【发布时间】:2020-12-28 22:31:09
【问题描述】:

我有一个功能,当我单击按钮时会触发该功能。根据 docId,我可以在 Firestore 中检索正确的下载 URL。但我也想更新我的“客户”集合中的特定字段(availableDownloads)。我无法让它工作。

此代码运行良好。它返回下载地址。

exports.getDownloadUrl = functions.https.onCall(async(data, context) => {
    var docRef= await db.collection('projects').doc(data.docId);
    return docRef.get().then(function(doc){
        const downloadURL = doc.data().downloadURL;
        return downloadURL;
    }).catch(function(error) {
        // Handle error
    });
});

然而事实并非如此。它返回 null

exports.getDownloadUrl = functions.https.onCall(async(data, context) => {
    var docRef= await db.collection('projects').doc(data.docId);
    return docRef.get()
    .then(async function(doc){
        const downloadURL = doc.data().downloadURL;
        const userRef = await db.collection('customers').doc(context.auth.uid);
        return userRef.update({
            availableDownloads: admin.firestore.FieldValue.increment(-1)
        }).then(()=> {
            return downloadURL;
        }).catch((error)=> {
            
        })
        
    }).catch(function(error) {
        // Handle error
    });
});

【问题讨论】:

  • 最好不要将 async/await 与 then/catch 混合使用。如果您能够在任何地方使用 async/await,请在任何地方使用它来简化您的代码。还可以考虑添加日志记录以了解此代码的实际执行方式。我们在这里看不到您的数据或任何变量。
  • 好的,谢谢,我可能会这样做。
  • 另外:这里不需要await await db.collection('customers').doc(context.auth.uid)
  • 是的,谢谢,我注意到了。感谢您的反馈。

标签: javascript node.js firebase google-cloud-firestore google-cloud-functions


【解决方案1】:

我首先想到的是.update() is an asynchronous function,所以你需要在userRef.update(...)前面加上一个await

您还应该考虑坚持使用箭头函数 (() => {}) 或匿名函数 (function () {}),但这只是一种风格说明。 :)

ETA:正如 Frank van Puffelen 指出的那样,您不需要只需awaits 即可简单地声明 DocumentReference,因为这些不是异步函数。 (看var docRef= await db.collection('projects').doc(data.docId);这一行)。

【讨论】:

    【解决方案2】:

    除了async/awaitthen/catch 混合使用的潜在问题外,您的主要问题是由于 variable scopingdownloadURL 变量是在第一个 then() 块中声明的局部变量,因此在第二个 then() 块中不可访问。

    用下面的代码可以很容易看出问题:

      function later(delay, value) {
        return new Promise((resolve) => setTimeout(resolve, delay, value));
      }
    
      later(500, 'value1')
        .then((value) => {
          console.log('First then block: ' + value);
          const downloadURL = value;
          return later(1000, 'value2');
        })
        .then((value) => {
          console.log('Second then block: ' + value);
          console.log('Second then block: ' + downloadURL); // Here it is going to throw an error
        })
        .catch((error) => {
          console.log(error);
        });
    

    需要在Cloud Function的作用域内声明downloadURL变量,如下:

    exports.getDownloadUrl = functions.https.onCall((data, context) => {
        let downloadURL;
        const docRef = db.collection('projects').doc(data.docId);
        return docRef.get()
            .then((doc) => {
                downloadURL = doc.data().downloadURL;
                const userRef = db.collection('customers').doc(context.auth.uid);
                return userRef.update({
                    availableDownloads: admin.firestore.FieldValue.increment(-1)
                });
            })
            .then(() => {
                return downloadURL;
            }).catch((error) => {
                // Handle error
            })
    });
    

    请注意,上面的代码没有使用 async/await。如果要使用 async/await,请执行以下操作:

    exports.getDownloadUrl = functions.https.onCall(async (data, context) => {
    
        try {
            const docRef = db.collection('projects').doc(data.docId);
    
            const doc = await docRef.get();
            const downloadURL = doc.data().downloadURL;
    
            const userRef = db.collection('customers').doc(context.auth.uid);
            await userRef.update({
                availableDownloads: admin.firestore.FieldValue.increment(-1)
            })
    
            return downloadURL;
        } catch (error) {
            // Handle error
        }
    
    });
    

    您可以看到,使用此代码,由于使用 async/await 对其进行了简化,因此不会因为变量作用域而变得更加复杂。

    【讨论】:

    • @HKN 您好,您有机会查看建议的解决方案吗?
    • 谢谢,它确实适用于我所做的声明。但是,按照您的方式这样做确实更有意义。
    • @HKN 感谢您的反馈。如果您认为我的回答给您带来了一些价值,请点赞。
    猜你喜欢
    • 2019-10-12
    • 2021-04-10
    • 2018-11-02
    • 2022-09-26
    • 2019-01-26
    • 2019-02-22
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    相关资源
    最近更新 更多