发送响应后继续执行(适用于 Firebase 的云函数)



我正在使用带有https触发器的Firebase Functions,我想知道在将响应发送到客户端后多久,这些函数会继续执行。我想向客户端发送响应,然后执行另一个操作(发送邮件)。 目前我这样做如下:

module.exports.doSomeJob = functions.https.onRequest((req, res) => {
doSomeAsyncJob()
.then(() => {
res.send("Ok");
})
.then(() => {
emailSender.sendEmail();
})
.catch(...);
});

上面的代码对我有用,但我怀疑代码之所以有效,是因为发送邮件在 res.send 完成之前已经完成,所以我想知道终止过程究竟是如何工作的,以确保代码不会中断。

您应该期望 HTTP 函数在您发送响应后的那一刻终止。 任何其他行为都是运气或竞争条件的某种组合。 不要编写依赖于运气的代码。

如果需要在工作完全完成之前向客户端发送响应,则需要启动第二个函数以从 HTTP 函数停止的位置继续。 使用发布/订阅函数来处理是很常见的。 让 HTTP 函数将发布/订阅消息发送到另一个函数,然后仅在消息发送后终止 HTTP 函数(通过发送响应)。

如果预期响应未与执行结果挂钩,则可以使用

module.exports.doSomeJob = functions.https.onRequest((req, res) => {
res.write('SUCCESS')
return doSomeAsyncJob()
.then(() => {
emailSender.sendEmail();
})
.then(() => {
res.end();
})
.catch(...);
});

这会在收到请求后立即发回响应,但使函数保持运行,直到调用res.end()您的客户端可以在收到响应后立即结束连接,但云函数将继续在后台运行 不确定这是否有帮助,但这可能是客户端需要在非常有限的时间内做出响应的解决方法,考虑到执行 pub/sub 本身需要一些额外的处理并且需要时间来执行

TL;博士

虽然https函数会在res.send()后不久终止,但不能保证res.send()后0行代码将被执行。

我认为更完整的答案有两个组成部分:

  1. 正如 Doug 指出的那样,不要在res.send()之后放置任何您希望执行的额外代码
  2. 云函数会在res.send()后不久终止,但不要指望正好执行0行代码

我遇到了这样一种情况:对于数据库维护脚本,如果没有记录符合我的标准,我会与res.send()说再见,并在它之后有额外的逻辑。我预计该部分不会运行,因为我已经终止了请求。

产生意外结果的示例:

exports.someFunction = functions.https.onRequest((req, res) => {
if (exitCriteria === true) {
// we can exit the function, nothing to do
console.log('Exit criteria met')
res.status(200).send()
}
// code to handle if someCriteria was falsy
console.log('Exit criteria not met, continue executing code')
})

在上面的示例中,我希望res.send()立即终止该功能 - 事实并非如此,第二个控制台.log也可能被击中 - 以及您可能拥有的任何其他代码。但是,这并不能保证,因此执行可能会在某个时候突然停止。

产生正确结果的示例:

exports.someFunction = functions.https.onRequest((req, res) => {
if (exitCriteria === true) {
// we can exit the function, nothing to do
console.log('Exit criteria met')
res.status(200).send()
}
else {
// code to handle if someCriteria was falsy
console.log('Exit criteria not met, continue executing code')
}
})

在此版本中,您将看到恰好 1 行 console.logs - 正如我最初打算的那样。

最新更新