Node.js 困惑于如何正确使用http Firebase云函数中的承诺
我不熟悉使用Firebase的云函数和http触发函数,我对如何正确终止该函数感到困惑。我不确定是应该使用res.sendStatus、返回承诺,还是两者兼而有之 我的函数的目标是循环浏览集合“communities”中的几个文档。每个社区都有一个文档集合,我在其中查询具有最高值“hotScore”的文档。然后,我将包含该文档的iOS推送通知发送到一个主题(给定社区中的所有用户) 不幸的是,在运行代码时,我遇到了几个错误,例如Node.js 困惑于如何正确使用http Firebase云函数中的承诺,node.js,firebase,google-cloud-functions,firebase-cloud-messaging,Node.js,Firebase,Google Cloud Functions,Firebase Cloud Messaging,我不熟悉使用Firebase的云函数和http触发函数,我对如何正确终止该函数感到困惑。我不确定是应该使用res.sendStatus、返回承诺,还是两者兼而有之 我的函数的目标是循环浏览集合“communities”中的几个文档。每个社区都有一个文档集合,我在其中查询具有最高值“hotScore”的文档。然后,我将包含该文档的iOS推送通知发送到一个主题(给定社区中的所有用户) 不幸的是,在运行代码时,我遇到了几个错误,例如错误[ERR\u HTTP\u HEADERS\u SENT]:在发送
错误[ERR\u HTTP\u HEADERS\u SENT]:在发送到客户端后无法设置头
和未处理的拒绝
。我很高兴,这是由于我在处理函数终止时的疏忽,尽管我已经被我到目前为止看到的在线资源弄糊涂了。有人介意看一下我的代码/给我指出正确的方向吗?非常感谢你
exports.sendNotificationTrendingPost = functions.https.onRequest(async (req, res) => {
//Get communities collection from Firestore
return admin.firestore().collection('communities').get().then((communities) => {
var communityPromises = [];
//Loop through each community
communities.forEach((community) => {
let communityID = community.get('communityID');
let communityName = community.get('name');
//Get the post with the highest hotScore
let communityPromise = admin.firestore().collection('communities').doc(communityID).collection('posts').orderBy('hotScore', 'desc').limit(1).get().then((posts) => {
let hottestPost = posts[0];
let postID = hottestPost.get('postID');
let postText = hottestPost.get('text');
let currentDate = Date.now() / 1000;
var message;
//Verify that the hottest post was posted in the past 24 hours
if (hottestPost.get('date') > (currentDate - 86400)) {
//Build the notification text (shortening if too long)
let shortenedPostText = postText.substring(0,60);
var textEnd = '';
if (postText.length > 60) {
textEnd = '...';
}
let notificationText = 'Trending post on ' + communityName + ': ' + shortenedPostText + textEnd;
//Build the push notification
message = {
apns: {
headers: {
'apns-push-type': 'alert'
},
payload: {
aps: {
alert: {
body: notificationText,
},
},
postID: postID,
},
},
topic: communityID
}
}
//Send the message and return the promise
if (message === null) {
return null;
} else {
return admin.messaging().send(message);
}
})
.catch(error => {
console.log(error);
res.status(500).send(error);
})
if (communityPromise !== null) {
communityPromises.push(communityPromise);
}
})
res.sendStatus(200);
return Promise.all(communityPromises);
})
.catch(error => {
console.log(error);
res.status(500).send(error);
})
})
正如samthecodingman所建议的,在您的情况下使用
async/await
要好得多,因为它将简化代码并使其更易于阅读
下面的更改应该可以做到这一点(未经测试)。请注意,我们如何使用社区名称数组将名称从一个循环传递到另一个循环。这是因为,对于,返回值是按照传递的承诺的顺序进行的,而不管完成顺序如何
exports.sendNotificationTrendingPost = functions.https.onRequest(async (req, res) => {
try {
const db = admin.firestore();
const communitiesQuerySnap = await db.collection('communities').get();
const communityPromises = [];
const communityNames = [];
communitiesQuerySnap.forEach((community) => {
let communityID = community.get('communityID');
let communityName = community.get('name');
communityNames.push(communityName);
communityPromises.push(db.collection('communities').doc(communityID).collection('posts').orderBy('hotScore', 'desc').limit(1).get())
});
const postsQuerySnapArray = await Promise.all(communityPromises);
const messagePromises = [];
postsQuerySnapArray.forEach((postsQuerySnap, index) => {
const hottestPost = postsQuerySnap.docs[0];
const postID = hottestPost.get('postID');
const postText = hottestPost.get('text');
const currentDate = Date.now() / 1000;
let message;
if (hottestPost.get('date') > (currentDate - 86400)) {
//Build the notification text (shortening if too long)
let shortenedPostText = postText.substring(0, 60);
var textEnd = '';
if (postText.length > 60) {
textEnd = '...';
}
const communityName = communityNames[index]; // The two Arrays postsQuerySnapArray and communityName have the same order, because Promise.all keeps the order.
let notificationText = 'Trending post on ' + communityName + ': ' + shortenedPostText + textEnd;
//Build the push notification
message = {
apns: {
headers: {
'apns-push-type': 'alert'
},
payload: {
aps: {
alert: {
body: notificationText,
},
},
postID: postID,
},
},
topic: communityID
}
messagePromises.push(admin.messaging().send(message));
}
})
await Promise.all(messagePromises);
res.status(200).send({ result: "completed" }); // Or res.end()
} catch (error) {
console.log(error);
res.status(500).send(error);
}
});
稍后我将回到这里,但应该注意的是,为HTTP请求函数返回
Promise
。乍一看,您可以调用res.status(500)。send(error)
,然后调用res.sendStatus(200)
。我会考虑重构你的代码,使用<代码> Aycy /<代码>等待语法,因为你已经进入了嵌套的承诺土地,这样会让你明白这一点。这是完全有意义的——我现在对承诺和Acthc/等待有了更好的理解。非常感谢您的时间和回答!:)还有一个问题-我在postsQuerySnap中调用某个元素上的get时遇到错误“TypeError:无法读取未定义”的属性“get”。在我的例子中,当我调用hottestPost.get('postID')或hottestPost.get('text')时会抛出错误。你知道是什么导致了这个问题吗?这可能是因为一个或多个社区没有hot score doc。您应该测试postQuerySnap是空的还是空的。我验证了所有社区都包含一个“posts”集合,并且每个post文档都有一个hotScore属性。问题似乎是postsQuerySnapArray中的每个postQuerySnap都是未定义的类型(甚至不是一个我可以测试它是否为空的数组)。啊,这是有道理的-不用担心!非常感谢您的帮助:)