是否有可能在文档更新之前退出/关闭该函数,从而使文档不更新?代码中的更多注释:
exports.enterToWin = functions.https.onCall(async (data) => {
const { user } = data;
try {
return await db.runTransaction(async (t) => {
const ref = db.collection(COLLECTION_NAME);
const snap = await ref.where('available', '==', true).get();
const prize = snap.docs[i];
// See how we're updating the prize here...
t.update(prize.ref, {
winner: user,
available: false
});
// ... but we're not waiting for the transaction to complete before returning?
// Could this cause the update to fail, in theory?
return { uid: doc.id, ...doc.data() };
});
} catch (error) {
return { error: 'Unexpected error: ' + error.message };
}
});
像这样等待会不会更安全/更好?
await t.update(prize.ref, {
winner: user,
available: false
});
return { uid: doc.id, ...doc.data() };
函数在99%的情况下工作良好。但是我们在生产中遇到了一些问题。一些用户获得了奖品,但奖品后来似乎消失了。试图解决实际发生的问题。由于
JavaScript异步函数必须等待,否则完全有可能在云函数终止之前承诺不会解决。这在Firebase文档Sync, async, and promises中有详细说明:
管理函数的生命周期以确保其正确解析是很重要的。通过正确终止函数,可以避免运行时间过长或无限循环的函数产生过多的费用。此外,您可以确保运行您的函数的Cloud Functions实例在您的函数成功达到其终止条件或状态之前不会关闭。
使用以下推荐的方法来管理功能的生命周期:
- 通过返回JavaScript承诺来解析执行异步处理的函数(也称为"后台函数")。
这在The Node.js Runtime documentation for Cloud Functions中有进一步的详细说明:
当处理涉及回调或
Promise
对象的异步任务时,你必须显式地通知运行时你的函数已经完成执行这些任务。您可以通过几种不同的方式实现这一点,如下面的示例所示。关键是你的代码必须等待异步任务或Promise
在返回之前完成;否则,函数的异步组件可能会在完成之前被终止。
在Tips &云函数的技巧文档:
不要启动后台活动
后台活动是指函数终止后发生的任何事情。函数调用一旦函数返回或发出完成信号就结束,例如在Node.js事件驱动函数中调用
callback
参数。在优雅终止后运行的任何代码都不能访问CPU,也不会取得任何进展。
直接回答你的问题,你必须await
调用update()
,因为它是一个异步函数。你应该配置你的IDE和/或linter来检查未等待的异步函数,例如,使用require-await:
该规则警告没有await表达式的异步函数。
规则的错误代码示例:
/*eslint require-await: "error"*/
async function foo() {
doSomething();
}
bar(async () => {
doSomething();
});
正确代码示例:
/*eslint require-await: "error"*/
async function foo() {
await doSomething();
}
bar(async () => {
await doSomething();
});
function foo() {
doSomething();
}
bar(() => {
doSomething();
});
// Allow empty functions.
async function noop() {}