【问题标题】:Firebase admin sequential call, return values and promisesFirebase 管理员顺序调用、返回值和承诺
【发布时间】:2018-10-23 05:10:33
【问题描述】:

0) 我有一个 HTTP 触发器:

exports.checkout = functions.https.onRequest((req, res) => {

1) 用户购买充值包时更新充值交易:

admin.database().ref('topupTransaction').push(topUpObject)

2) 获取用户对象(有账户余额)

admin.database().ref('/users/' + userID).once("value",snap=> {

3) 设置新的用户对象(带有新的账户余额)

admin.database().ref('/users/' + req.query.userUid).set(topUpObject);

我不确定如何按顺序运行所有这些 (1,2,3) 并将值返回给客户端 (0)。击球不应该嵌入承诺。有 Promise.all,但是在这种情况下如何使用它。

点击“避免嵌套承诺”。当我尝试这样做时:

exports.checkout = functions.https.onRequest((req, res) => { var nonceFromTheClient = req.body.payment_method_nonce;

  var topUpObject = {
    amount : parseInt(req.query.topUpPackage),
    date : admin.database.ServerValue.TIMESTAMP, // 1525451616097
    user : req.query.userUid
  };

  admin.database().ref('topupTransaction').push(topUpObject)
  .then((topUpResult) => {
    return admin.database().ref('/users/' + userID).once("value");
  }).then((oldUserData)=>{
    return admin.database().ref('/users/' + req.query.userUid).set(topUpObject).then((newUserData)=>{
      return res.send(newUserData.val());
    })
      ;
  }).catch((error) => {
    // Update databse failed (top up transaction)
    console.log('Error sending message:', error);
    return res.status(500).send(error);
  });

更新

使用 Promise.all,但部分工作出现错误:

// Create 2 functions
function asyncFunction1(topUpObject){
    // Push top up object to database
    admin.database().ref('topupTransaction').push(topUpObject)
      .then((response) => {
        // Update databse successful (top up transaction)
        console.log('Top Up transaction created successfully!', topUpObject);
        // return res.redirect(303, response.ref);
        return topUpObject;
      }).catch((error) => {
        // Update databse failed (top up transaction)
        console.log('Error sending message:', error);
        return error;
      });
}

function asyncFunction2(userID,topUpObject){
    // Get the user account balance
    console.log('Current User ID: ', userID);
    var ref = admin.database().ref('users').child(userID);
    admin.database().ref('/users/' + userID).once("value",snap=> {
      // do some stuff once
      console.log('Current User Data',snap.val());
      console.log('Current User balance',snap.val().accountBalance);
      var userContents = snap.val();
      var currentBalance = userContents.accountBalance;
      var updatedBalance = currentBalance + topUpObject.amount;
      console.log('Updated Balance',updatedBalance);
      userContents.accountBalance = updatedBalance;
      /*Current User Data { 
        accountBalance: 0,
        accountCurrency: 'MYR',
        createdOn: '2018-05-02T20:42:49Z',
        phoneNumber: '+123445555555'
      }
      */
      admin.database().ref('/users/' + userID).set(userContents).then(snapshot => {
        console.log('Updated top up value! for user', topUpObject);
        return res.send(topUpObject.amount);  
      }).catch((error) => {
        // Update databse failed (top up transaction)
        console.log('Error sending message:', error);
        return error;
      });

    });
}

// app.post("/checkout", function (req, res) {
exports.checkout = functions.https.onRequest((req, res) => {
  var nonceFromTheClient = req.body.payment_method_nonce;
  // Use payment method nonce here

  // Create Transaction
  gateway.transaction.sale({ 
    amount: req.query.topUpPackage,
    paymentMethodNonce: nonceFromTheClient,
    options: {
      submitForSettlement: true
    }
  },(err, result) => { //TODO: What should we pass back here???
    if (err) {
    // If top up error (from braintree)
      console.log(err.stack);
    }else{
    // If top up is successful
      console.log('Result:',result);
      console.log('Top Up Package is: ', req.query.topUpPackage);
      var topUpObject = {
        amount : parseInt(req.query.topUpPackage),
        date : admin.database.ServerValue.TIMESTAMP, // 1525451616097
        user : req.query.userUid
      };
      return Promise.all([asyncFunction1(topUpObject), asyncFunction2(req.query.userUid,topUpObject)]); //TODO: how to pass back res() to client???
    }
    // Return the error as response
    return res.send(err);
  });
});

exports.client_token = functions.https.onRequest((req, res) => {

    // a token needs to be generated on each request
    // so we nest this inside the request handler

    // Need to write a function to return a client token,
    // and return it back by using the res.send command
    console.log('Log customerId',req.query.text);

    gateway.clientToken.generate({
      // customerId: req.query.text
    }, (err, response) => {
      // error handling for connection issues
      if (err) {
        console.log(err.stack);
      }else{
        clientToken = response.clientToken;
        console.log('Log Client token:',clientToken);
        return res.send(clientToken);
      }
      return res.send(err);
    });
    // return null;
});

【问题讨论】:

  • @Doug Stevenson,看过你的 YouTube 视频 youtube.com/watch?v=d9GrysWH1Lc。非常喜欢视频。您是否有任何视频更详细地解释了 Promise.all 返回值?
  • Promise.all 返回什么?

标签: node.js firebase google-cloud-functions firebase-admin


【解决方案1】:

依我的拙见,您的代码中有几处需要微调,如下所示。我已经在代码中注释了。

但是,请注意,由于我们不知道topUpObject 来自哪里(以及它是什么),因此更精确有点困难。如果您分享更多详细信息(完整的 Cloud Function 代码和数据库结构),我可能会更准确。

admin.database().ref('topupTransaction').push(topUpObject)
    .then(topUpResult => {
        return admin.database().ref('/users/' + userID).once("value");
    })
    .then(oldUserData => {
         return admin.database().ref('/users/' + req.query.userUid).set(topUpObject);  
         //Set returns a non-null firebase.Promise containing void
    })
    .then(() => {  //so I don't think you can get the newUserData here
        //return res.send(newUserData.val());  <- and therefore you cannot do that here
        //But if I understand well you want to send back to the user the topUpObject, so do as follows:
        return res.status(200).send(topUpObject);  //<- note the addition of status(200) here
    })
    .catch((error) => {
        // Update databse failed (top up transaction)
        console.log('Error sending message:', error);
        return res.status(500).send(error);
});

【讨论】:

  • 嗨@Renaud Tarnec,在客户端,用户正在购买充值包,例如50 美元。在 firebase 后端,它需要做 3 件事……第一个是创建交易,第二个是获取当前用户数据(带有帐户余额)并在当前帐户余额中添加 50 美元,第三个是更新用户新余额的数据。你知道如何使用 Promise.all 来实现这一点吗?我正在使用braintree sdk
  • 不确定是否必须使用 Promise.all。你能分享你的整个代码吗?
  • 更新了代码。请帮忙 :D 不熟悉 Promise.all。
  • 我认为(再次,恕我直言)你混淆了承诺链和 Promise.all。当一个“有一系列异步任务要一个接一个地完成”时使用链接(参见javascript.info/promise-chaining)。 Promise.all 以某种方式专用于并行操作:它返回一个承诺,当数组中的所有项目都被满足时,它就会被满足。见stackoverflow.com/questions/38180080/when-to-use-promise-all。我不确定您是否要并行执行 asyncFunction1 和 asyncFunction2。我建议你使用 then 链接来重构你的整个过程。
  • 我想并行执行 asyncFunction1 和 asyncFunction2。但是在 asyncFunction2 中,我想将它链接到 database().ref()。 once() 和 admin.database().ref().set()。将查看您共享的链接并尝试链接。非常感谢。
猜你喜欢
  • 2018-11-09
  • 2018-03-06
  • 2016-06-15
  • 1970-01-01
  • 2016-11-23
  • 2018-09-27
  • 1970-01-01
  • 2016-08-18
  • 2015-12-22
相关资源
最近更新 更多