【问题标题】:How to stop execution of a Firebase-triggered Firebase Function?如何停止执行 Firebase 触发的 Firebase 函数?
【发布时间】:2020-04-18 07:06:46
【问题描述】:

背景

据我所知,Firebase 触发的函数可以仅从一个触发事件运行多次(我想我可以从 Firebase 的日志中看到这一点)。

由于这可能是一种数据损坏行为,我想实现一些标志,如果它发现标志是true,它允许函数停止。

在数据库中创建该对象后,我希望这个函数(Typescript)只运行一次。

例如:

adm.database().ref('/Users/{UserID}')

我打算在数据库中为每个用户节点存储一个键值对:

{"alreadyTriggered":true}

或者将'触发器'存储在单独的节点中,没关系。

然后当 Firebase 决定触发相应的函数时,如果不是第一次运行,我将进行检查并停止该函数的执行:

data = snapshot.val();
if (data.alreadyTriggered) {
    return;
}
// The function continues here

实施

(有些括号和括号可能会丢失,这是一个在浏览器窗口中编写的伪代码-Typescript)

index.ts:

    import * as u from './users';
    import * as ff from 'firebase-functions';
    import * as adm from 'firebase-admin';
    import * as types from './types';

    // Triggered function
    export const userCreated = ff.database.ref('/Users/{userId}').onCreate((sn, ctx) => {
      let user: types.User = sn.val();
      console.log(`New user created, User Id: ${ctx.params.userId}`);
      return u.onCreated(user);
    });

users.ts:

// Function implementation

export function onCreated(user: types.User): Promise<void> {
    return adm.database().ref("/Triggers").once('value', snapshot => {
        let data: any = snapshot.val();
        const alreadyTriggered: boolean = data.alreadyTriggered; 
    }).then(alreadyTriggered => {
        if (alreadyTriggered) {
            // We would like to stop any execution here
            return
        }
        else {
            console.log(`Continuing processing`)
        }
    }).then(() => {
        // This code should be reached only if alreadyTriggered is set to false
        // do actual work with the user object
        //...someNewData
        //then save it
        adm.database().ref('/Users').child(user.userId).update(someNewData)
    })
}

问题

问题是我不知道如何可靠地停止执行该函数。我曾尝试使用return(不起作用)、break(仅用于循环),然后我开始使用throw new Error('Stopping function execution'),但它似乎并没有可靠地停止——有时我在日志中看到该函数继续执行,尽管alreadyTriggered 肯定是true

问题

如何从其代码中停止执行 Firebase Typescript 函数?

【问题讨论】:

  • 请阅读文档中关于idempotence 的部分。您将需要一个实现幂等性的策略,但是有很多方法可以做到这一点,具体取决于您的功能的需要。您在此处显示的信息不足以说明需要什么,或者您尝试过的内容没有按您期望的方式工作。考虑显示不起作用的整个函数,而不仅仅是几行。
  • 道格,非常感谢您的评论。我已经阅读了关于幂等性的文档,但是我能找到的关于停止的唯一提示是调用一些我找不到的 Node 回调。我已经按照您的建议提供了整个功能,谢谢!
  • 我看过关于幂等性的视频(你提供的链接),谢谢。但是,它描述了一种避免多次运行 _same_instance_of_the_same_function 的策略(通过上下文),但我想确保即使它是对同一函数的不同调用,如果一个调用已经在运行,我也会停止所有其他调用.到目前为止,我能够通过返回 return Promise.reject("Stopping") 可靠地阻止它,但我不确定这是一个优雅的解决方案。
  • 您必须将一些数据存储在数据库或其他持久性存储中,以便其他调用可以检查以防止函数完成。
  • 是的,这就是我使用 /Triggers 并检查 alreadyTriggered 值的原因。但是一旦函数发现它是true - 如何停止函数中其他代码的执行?

标签: javascript typescript firebase firebase-realtime-database google-cloud-functions


【解决方案1】:

您的return 语句未从顶级函数返回。它只是从您传递给then() 的 lambda 函数返回。您必须将信息沿着 Promise 链传播,以告知下一个回调是否执行其工作。

return adm.database().ref("/Triggers").once('value', snapshot => {
    let data: any = snapshot.val();
    const alreadyTriggered: boolean = data.alreadyTriggered; 
}).then(alreadyTriggered => {
    if (alreadyTriggered) {
        // We would like to stop any execution here
        return false
    }
    else {
        console.log(`Continuing processing`)
        return true
    }
}).then((continue) => {
    // This code should be reached only if alreadyTriggered is set to false
    // do actual work with the user object
    //...someNewData
    //then save it
    if (continue) {
        return adm.database().ref('/Users').child(user.userId).update(someNewData)
    }
})

您还错过了上述update 返回的承诺,这将导致您的函数超时。

请注意,使用 async/await 语法会更容易。

【讨论】:

  • 为什么我们不能在没有任何参数的情况下运行最后一个.then成功回调? IE。运行.then( =&gt; { //do actual work... 而不是.then((continue) =&gt;?停止或不停止是在上一步中注意的。或者return false 只是将false 提供给下一个.then?我不确定return false 到底做了什么。
  • 另外,我认为链接多个 thens 是 Firebase Functions 的好习惯。您能否参考一些很好的例子来说明在这种情况下 async/await 语法如何更好?谢谢
猜你喜欢
  • 2018-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
  • 2018-09-14
  • 1970-01-01
  • 2018-03-15
相关资源
最近更新 更多