【问题标题】:how to use Firebase Cloud Functions with promises and forEach?如何使用带有 Promise 和 forEach 的 Firebase Cloud Functions?
【发布时间】:2018-10-11 02:16:54
【问题描述】:

我在这里尝试做两件事。 1) 向所有员工发送通知。 2)将特定的参考复制到 员工 ID 参考。如果不存在特殊参考,我将复制一般参考。 该程序运行没有错误。事实上它的完美。但有时我会收到通知代码部分的超时错误。

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

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

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!");
});

【问题讨论】:

  • 我相信您已经正确地确定了 forEach 循环是您问题的核心——很可能,循环的每次迭代都会启动另一个未返回的承诺链——我想你再次经历了失信的承诺..
  • 你其实是对的......有时日志会打印 2-3 次相同的结果。

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


【解决方案1】:

为@ChaseMoskal 答案添加一个非常重要的步骤。 对于将 TypeScript 与 Firebase 一起使用的用户,由于 Firebase 服务器未在 NodeJs 中运行 v8+,因此您很有可能会收到此错误:

“TypeError:snapshots.map is not a function”……就行了:await Promise.all(snapshots.map(handleSnapshot))。

这在您的 tsconfig.json 中可能是 "lib": ["es6"]。在这种情况下,只需将这个小 sn-p 添加到接受的答案中,即可将 Firebase Datasnapshot 放入可与 .map(...) 一起使用的数组中

加长版:

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!")
})

短版:

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

   let data_snap_arr = Object.keys(snapshots.val() || {}) .map(k => snapshots.val()[k]);

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

【讨论】:

  • 我实际上已经放弃了这个,但现在可以使用接受的答案
【解决方案2】:

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

dropped promises 是最常见和最难调试的问题之一,我已经看到了我的公平份额

对代码的重要更改:

  1. 现代 async 语法

    • 让 promise 组织起来更简洁
  2. 使用Promise.all 而不是forEach

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

    • 我不太确定这些是什么库
    • 他们似乎接受了基于承诺的用法
    • 所以我消除了回调的使用以支持承诺
  4. 请查看 TODO 标记

    • 不太确定 else 块的用途,所以一定要修补它

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!")
})

【讨论】:

  • @ChaseMoskal,我已将我的 Node 版本更新到 v10。并更新了我所有的依赖项。一切似乎都是正确的,除了同样的事情。它不允许我在“functions.https.onRequest(async(request, response) =>”中使用“async”...有什么方法可以在不将 async 放入该行的情况下运行它。因为我找不到任何文档或在“.onRequest(”... 和...“(request,responce)=>”之间放置“async”的在线示例
  • 我们不能使用 foreach,我们不是在循环,我们实际上是在提供一个回调
  • 我也不想回到“.thens”---但是已经一个多星期了,我已经在线检查了每个文档,并且没有一个资源显示“功能。 https.onRequest(async(request, response) =>"... 而且我从来没有使用过这个,所以我不可能更正这段代码!我已经更新了我所有的东西,节点/依赖项/firebase 工具等。 .我再次问你,“你确定我们可以在那个http函数的第一行加上‘async’这个词吗??
  • 我正在查看我的“node_modules”文件夹,只能找到“async”和“asynckit”,但没有找到 Asyncawait 或 Async-await...对于这种类型的 async/wait,这是正确的代码?
  • @ChaseMoskal,除了你的代码之外,我尝试了另一个与异步等待有关的示例。结果是一样的。它从未允许我将其部署到 Firebase 云服务器。语法错误。 EsLint 也没有显示错误。它与拥有旧版本节点并尝试运行异步的错误非常相似。但我的版本是 10!
猜你喜欢
  • 2018-04-18
  • 1970-01-01
  • 1970-01-01
  • 2021-01-09
  • 2021-10-25
  • 2017-08-08
  • 2017-12-24
  • 2019-01-27
  • 2020-03-15
相关资源
最近更新 更多