【问题标题】:Function returned undefined, expected Promise or value in Cloud Functions函数在 Cloud Functions 中返回未定义、预期的 Promise 或值
【发布时间】:2021-01-09 09:25:26
【问题描述】:

我有一个在 Firestore 中创建文档时触发的云函数,以及 I keep getting Function returned undefined, expected Promise or value。该函数完成了它应该做的事情,但有时需要大约 25-30 秒,所以我认为它可能与这个错误有关。如果有人可以帮助我了解返回这里的内容,我将不胜感激。我的功能如下:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();

const Iyzipay = require('iyzipay');

const iyzipay = new Iyzipay({
  apiKey: '...',
  secretKey: '...',
    uri: '...'
});

exports.pay = functions
.region('europe-west1')
.firestore
.document('requests/{docId}')
.onCreate((snap, context) => {
const newValue = snap.data();
       const request = {
        locale: Iyzipay.LOCALE.TR,
        conversationId: newValue.uid,
        price: newValue.price,
        paidPrice: newValue.price,
        currency: Iyzipay.CURRENCY.TRY,
        installment: '1',
        basketId: 'B67832',
        paymentChannel: Iyzipay.PAYMENT_CHANNEL.MOBILE_IOS,
        paymentGroup: Iyzipay.PAYMENT_GROUP.LISTING,
        paymentCard: {
            cardHolderName: newValue.cardHolderName,
            cardNumber: newValue.cardNumber,
            expireMonth: newValue.expireMonth,
            expireYear: newValue.expireYear,
            cvc: newValue.cvc
        },
        buyer: {
            id: newValue.uid,
            name: newValue.name,
            surname: newValue.surname,
            gsmNumber: newValue.gsmNumber,
            email: newValue.email,
            identityNumber: newValue.identityNumber,
            registrationAddress: newValue.registrationAddress,
            city: newValue.city,
            country: newValue.country,
            zipCode: newValue.zipCode
        },
        shippingAddress: {
            contactName: newValue.name,
            city: newValue.city,
            country: newValue.country,
            address: newValue.registrationAddress,
            zipCode: newValue.zipCode
        },
        billingAddress: {
          contactName: newValue.name,
          city: newValue.city,
          country: newValue.country,
          address: newValue.registrationAddress,
          zipCode: newValue.zipCode
        },
        basketItems: [
            {
                id: newValue.productid,
                name: newValue.productname,
                category1: newValue.category1,
                itemType: Iyzipay.BASKET_ITEM_TYPE.PHYSICAL,
                price: newValue.price
            },
        ]
       }
       iyzipay.payment.create(request, function (err, result) {
        console.log(err, result);
  
       const docRef1 = db.collection('results').doc().set(result);
    }) 
      })

【问题讨论】:

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


    【解决方案1】:

    您需要在所有异步工作完成后终止 Cloud Function,请参阅doc。对于后台触发的 Cloud Function(例如 Cloud Firestore function onCreate trigger,就像您的 Cloud Function),您必须返回异步方法调用返回的 chain of Promises

    我不知道Iyzipay 服务和对应的Node.js 库,但似乎没有“承诺”版本的iyzipay.payment.create 方法。因此,您应该将其包装在 Promise 中,并将此 Promise 与 Firestore 异步 set() 方法返回的 Promise 链接起来,如下所示(未经测试)。

    exports.pay = functions
        .region('europe-west1')
        .firestore
        .document('requests/{docId}')
        .onCreate((snap, context) => {
            const newValue = snap.data();
            const request = { ... };
    
            return new Promise(function (resolve, reject) {
                iyzipay.payment.create(request, function (err, result) {
                    if (err) {
                        reject(err)
                    } else {
                        resolve(result)
                    }
                })
            })
            .then(result => {
                return db.collection('results').doc().set(result);
            })
            .catch(error => {
                console.log(error);
                return null;
            });
    
        });
    

    如果要在操作完成后向日志中写入内容,请执行以下操作:

        // ...
        .onCreate((snap, context) => {
            const newValue = snap.data();
            const request = { ... };
    
            return new Promise(function (resolve, reject) {
              // ...
            })
            .then(result => {
                return db.collection('results').doc().set(result);
            })
            .then(() => {
                console.log("Operation completed: " + result);
                return null;
            })
            .catch(error => {
                console.log(error);
                return null;
            });
    

    根据您的评论更新:

    如何在return db.collection('results').doc().set(result); 之后添加另一个 Firestore 查询?例如,我想 更新文档中的字段,那么我可以在哪里添加 db.collection('listings').doc(newValue.productid).update({sold : true})?

    你有两种可能:

    方法#1

    由于 update() 方法是一个返回 Promise 的异步方法(与所有 Firebase 异步方法一样),您需要将其添加到 Promise 链中,如下所示:

            return new Promise(function (resolve, reject) {
                iyzipay.payment.create(request, function (err, result) {
                    if (err) {
                        reject(err)
                    } else {
                        resolve(result)
                    }
                })
            })
            .then(result => {
                return db.collection('results').doc().set(result);
            })
            .then(() => {
                return db.collection('listings').doc(newValue.productid).update({sold: true});
            })
            .catch(error => {
                console.log(error);
                return null;
            });
    

    方法#2

    由于set()update() 方法都写入文档,您可以使用batched write,如下所示:

            return new Promise(function (resolve, reject) {
                iyzipay.payment.create(request, function (err, result) {
                    if (err) {
                        reject(err)
                    } else {
                        resolve(result)
                    }
                })
            })
            .then(result => {
               const batch = db.batch();
    
               var docRef1 = db.collection('results').doc();
               batch.set(docRef1, result);
    
               var docRef2 = db.collection('listings').doc(newValue.productid);
               batch.update(docRef2, {sold: true});
    
               return batch.commit();                
            })
            .catch(error => {
                console.log(error);
                return null;
            });
    

    与方法 #1 的不同之处在于,两次写入是在一个原子操作中完成的。


    PS:请注意,您可以使用db.collection('results').add(result);,而不是db.collection('results').doc().set(result);

    【讨论】:

    • 一开始它返回:Error: functions predeploy error: Command terminated with non-zero exit code1我最后加了return console.log(result),它成功了!
    • 是的,我这边有一个BIG错误!我忘了返回由set() 方法返回的承诺......真可惜......在我的回答中我一直解释说你需要返回承诺链并且代码没有遵循这个建议! :-) 现在已经适应了。我还添加了完成后使用控制台日志记录的详细信息。如果它解决了您的问题,您可能会接受答案,请参阅stackoverflow.com/help/someone-answers
    • 别担心!!谢谢回复。我有最后一个问题:如何在return db.collection('results').doc().set(result); 之后添加另一个 Firestore 查询?比如我要更新文档中的一个字段,那么在哪里可以添加db.collection('listings').doc(newValue.productid).update({sold : true})
    猜你喜欢
    • 1970-01-01
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 2018-07-20
    • 1970-01-01
    • 2019-08-06
    • 2019-11-18
    相关资源
    最近更新 更多