如何将Firebase云功能与promises和forEach一起使用?

时间:2018-05-01 00:00:01

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

我想在这里做两件事。 1)向所有员工发送通知。 2)将特定参考复制到    员工id ref。如果没有特殊参考,我将复制一般参考。 该程序运行没有错误。事实上它是完美的。但有时我会在通知代码部分出现Timed out错误。

  

错误:fcm.googleapis.com网络超时。请再试一次。

将一个引用复制到另一个引用的代码始终有效,从未在那里收到错误。 我觉得错误是由于没有正确处理forEach的承诺。你能用正确放置的Promises来帮助我让这个代码完美无缺吗?

exports.myFunc = functions.https.onRequest( (request, response) => {

admin.database().ref('/Employees').once('value').then(function(snap) {
    snap.forEach(function (snapshot) {
        var obj = snapshot.val();

        if(obj.department){//only go ahead if dept is present
            console.log(' : ' + obj.department);
            var id, tkid, dept;
            id = obj.empId; tkid = obj.tokenId; dept = obj.department;

            var welcomeStr="hello! Welcom to our Department";

            //================================================================notifications
            var payload = {
                data: {
                  greeting: welcomeStr,
                  to_who: id
                }
              };
                    admin.messaging().sendToDevice(tkid,payload)
                    .then(function(response){
                        console.log("Successfully sent message: ", response);
                    })
                    .catch(function(error){
                            console.log("Error sending message: ", error);
                    })
            //===================================================Ref copying

            var destinationRef = admin.database().ref('/Employees/' + id);//final destination
            var option2Ref = admin.database().ref('/Company/General');//when special doesnt exist
            var option1Ref = admin.database().ref('/Company/Special');//if special exists

            option1.once('value', function(snapshot1){
                if (snapshot1.exists()){//copy from straing from option11 to Employees/id
                    option1.once('value', function(snap)  {
                        destinationRef.set( snap.val(), function(error) {
                            if( error && typeof(console) !== 'undefined' && console.error ) {  console.error(error); }

                            console.log('DONE ....  ' + id);
                        });
                    });
                }

                else{//we need to copy from option2 to Employees/id
                    option2Ref.once('value', function(snap)  {
                        newRef.set( snap.val(), function(error) {
                            if( error && typeof(console) !== 'undefined' && console.error ) {  console.error(error); }

                            console.log('DONE .... ' + id);
                        });
                    });
                }

            });
        }
        else{
            console.log('No Department: ' + obj.dept);
            return;
        }
    });

 });


response.send("WOKAY!");
});

2 个答案:

答案 0 :(得分:3)

这里我已经重写了你的代码,希望能够清理复杂的承诺链

掉线承诺是调试中最常见和最困难的问题之一,我已经看到了我的公平份额

您的代码的重要更改:

  1. 现代async语法

    • 以便承诺更清晰地组织
  2. 使用Promise.all代替forEach

    • 这样就可以等待每一个承诺而不被遗忘
    • (希望)所有承诺都正确归还
    • 所有快照操作同时运行,并且onRequest处理程序应该等到它们全部完成后再终止
  3. 使用onceset的承诺而不是回调

    • 我不确定这些库是什么
    • 看起来他们接受基于承诺的使用
    • 所以我取消了回复使用以支持承诺
  4. 请查看TODO标记

    • 不确定那个意图阻止了什么,所以一定要修补
  5. async function handleSnapshot(snapshot) {
      const {empId, tokenId, department} = snapshot.val()
    
      // only go ahead if dept is present
      if (!department) throw new Error("no department")
      console.log("department:", department)
    
      //================================================================notifications
      const payload = {
        data: {
          greeting: "Hello! Welcome to our Department",
          to_who: empId
        }
      }
      const response = await admin.messaging().sendToDevice(tokenId, payload)
      console.log("successfully sent message", response)
      //===================================================Ref copying
    
      const destinationRef = admin.database().ref('/Employees/' + empId) // final destination
      const option2Ref = admin.database().ref('/Company/General') // when special doesnt exist
      const option1Ref = admin.database().ref('/Company/Special') // if special exists
    
      const snapshot1 = await option1Ref.once("value")
    
      // copy from string from option1 to Employees/id
      if (snapshot1.exists()) { 
        await destinationRef.set(snapshot1.val())
        console.log("DONE1...", empId)
      }
    
      // TODO review this block
      // we need to copy from option2 to Employees/id
      else {
        const snapshot2 = await option2Ref.once("value")
        await destinationRef.set(snapshot2.val())
        console.log("DONE2...", empId)
      }
    }
    
    exports.myFunc = functions.https.onRequest(async(request, response) => {
      const snapshots = await admin.database().ref('/Employees').once('value')
      await Promise.all(snapshots.map(handleSnapshot))
      response.send("WOKAY!")
    })
    

答案 1 :(得分:1)

向@ChaseMoskal回答添加一个非常重要的步骤。 对于使用TypeScript和Firebase的用户,由于firebase服务器没有在NodeJs中运行v8 +,因此很可能会出现此错误:

  

“TypeError:snapshots.map不是函数”......在线:等待Promise.all(snapshots.map(handleSnapshot))。

这会导致你的tsconfig.json可能“lib”:[“es6”] 。在这种情况下,只需将此小片段添加到接受的答案中,即可将Firebase数据快照放入可与.map(...)

一起使用的数组中。

版本更长:

Checkable

缩短版本:

exports.myFunc = functions.https.onRequest(async(request, response) => {
const snapshots = await admin.database().ref('/Employees').once('value')

    var data_snap_arr = [];
        snapshots.forEach(function(child_Snapshot) {
            var stuff = child_Snapshot.val();
            stuff.key = child_Snapshot.key;
            data_snap_arr.push(stuff);

await Promise.all(data_snap_arr.map(handleSnapshot))
response.send("WOKAY!")
})