Javascript Firebase云函数中的嵌套HTTP请求
我正在使用HTTP触发的Firebase云函数发出HTTP请求。我从Meetup.com获取一系列结果(事件),并将每个结果推送到Firebase实时数据库。但是对于每个结果,我还需要对一条额外的信息(主持事件的组的类别)发出另一个HTTP请求,以将其折叠到我为该事件推送到数据库的数据中。这些嵌套请求会导致云函数崩溃,并出现我无法理解的错误Javascript Firebase云函数中的嵌套HTTP请求,javascript,node.js,firebase,firebase-realtime-database,google-cloud-functions,Javascript,Node.js,Firebase,Firebase Realtime Database,Google Cloud Functions,我正在使用HTTP触发的Firebase云函数发出HTTP请求。我从Meetup.com获取一系列结果(事件),并将每个结果推送到Firebase实时数据库。但是对于每个结果,我还需要对一条额外的信息(主持事件的组的类别)发出另一个HTTP请求,以将其折叠到我为该事件推送到数据库的数据中。这些嵌套请求会导致云函数崩溃,并出现我无法理解的错误 const functions = require("firebase-functions"); const admin = require("fireba
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const request = require('request');
exports.foo = functions.https.onRequest(
(req, res) => {
var ref = admin.database().ref("/foo");
var options = {
url: "https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=****",
json: true
};
return request(
options,
(error, response, body) => {
if (error) {
console.log(JSON.stringify(error));
return res.status(500).end();
}
if ("results" in body) {
for (var i = 0; i < body.results.length; i++) {
var result = body.results[i];
if ("name" in result &&
"description" in result &&
"group" in result &&
"urlname" in result.group
) {
var groupOptions = {
url: "https://api.meetup.com/" + result.group.urlname + "?sign=true&photo-host=public&key=****",
json: true
};
var categoryResult = request(
groupOptions,
(groupError, groupResponse, groupBody) => {
if (groupError) {
console.log(JSON.stringify(error));
return null;
}
if ("category" in groupBody &&
"name" in groupBody.category
) {
return groupBody.category.name;
}
return null;
}
);
if (categoryResult) {
var event = {
name: result.name,
description: result.description,
category: categoryResult
};
ref.push(event);
}
}
}
return res.status(200).send("processed events");
} else {
return res.status(500).end();
}
}
);
}
);
如果我省略了获取组类别的部分,其余的代码就可以正常工作(只需将每个事件的名称和描述写入数据库,没有嵌套的请求)。那么,正确的方法是什么呢?我怀疑这个问题是由于回调造成的。当您使用firebase函数时,导出的函数应该等待所有内容执行,或者在所有内容完成执行后返回解析的承诺。在这种情况下,导出的函数将在其余执行完成之前返回 这是一个更加基于承诺的开始-
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const request = require("request-promise-native");
exports.foo = functions.https.onRequest(async (req, res) => {
const ref = admin.database().ref("/foo");
try {
const reqEventOptions = {
url:
"https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=xxxxxx",
json: true
};
const bodyEventRequest = await request(reqEventOptions);
if (!bodyEventRequest.results) {
return res.status(200).end();
}
await Promise.all(
bodyEventRequest.results.map(async result => {
if (
result.name &&
result.description &&
result.group &&
result.group.urlname
) {
const event = {
name: result.name,
description: result.description
};
// get group information
const groupOptions = {
url:
"https://api.meetup.com/" +
result.group.urlname +
"?sign=true&photo-host=public&key=xxxxxx",
json: true
};
const categoryResultResponse = await request(groupOptions);
if (
categoryResultResponse.category &&
categoryResultResponse.category.name
) {
event.category = categoryResultResponse.category.name;
}
// save to the databse
return ref.push(event);
}
})
);
return res.status(200).send("processed events");
} catch (error) {
console.error(error.message);
}
});
对更改的快速概述-
- 使用await和async调用来等待事情完成,而不是在回调中触发(async和await通常比承诺更容易阅读。然后函数的执行顺序就是代码的顺序)
- 使用request-promise-native,它支持承诺/等待(即等待意味着等待,直到承诺返回,所以我们需要返回承诺的东西)
- 使用常量和let vs.var作为变量;这改进了变量的范围
- 不要像if(is good){do good things}那样进行检查,而是使用if(isbad){return some error}do good thin。这使代码更易于阅读,并防止出现大量嵌套的if,因为您不知道它们的结尾在哪里
- 使用Promise.all()以便并行地检索每个事件的类别
- 由于
不返回承诺,您需要为request
使用接口包装器,以便正确链接不同的异步事件(参见Doug对您的问题的评论)request
- 由于随后将使用
多次(并行)调用不同的端点,因此需要使用request promise
,以便在发送回响应之前等待所有承诺的解决。对Firebasepromise.all()
方法的不同调用也是如此push()
事件
对象的name
和description
的值。结果
数组中项目的顺序与承诺
数组中项目的顺序完全相同。因此,您应该能够在results.forEach(groupBody=>{})中获取name
和description
的值,例如,将这些值保存在全局数组中
const functions=require('firebase-functions');
const admin=require('firebase-admin');
admin.initializeApp();
var rp=要求(“要求-承诺”);
exports.foo=functions.https.onRequest((req,res)=>{
var ref=admin.database().ref('/foo');
变量选项={
网址:
'https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=***',,
json:true
};
rp(选项)
。然后(body=>{
如果(正文中的“结果”){
常量承诺=[];
对于(var i=0;i{
常量承诺=[];
results.forEach(groupBody=>{
if(groupBody中的“类别”和groupBody.category中的“名称”){
var事件={
姓名:‘……’,
说明:“…”,
类别:groupBody.category.name
};
承诺推送(参考推送(事件));
}否则{
抛出新错误('err xxxx');
}
});
返回承诺。全部(承诺);
})
.然后(()=>{
res.send(“已处理事件”);
})
.catch(错误=>{
资源状态(500)。发送(错误);
});
});
我做了一些更改,让它与节点8一起工作。我将此添加到我的包.json
:
"engines": {
"node": "8"
}
这就是代码现在的样子,基于R.Wright和一些Firebase云函数示例代码
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const request = require("request-promise-native");
exports.foo = functions.https.onRequest(
async (req, res) => {
var ref = admin.database().ref("/foo");
var options = {
url: "https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=****",
json: true
};
await request(
options,
async (error, response, body) => {
if (error) {
console.error(JSON.stringify(error));
res.status(500).end();
} else if ("results" in body) {
for (var i = 0; i < body.results.length; i++) {
var result = body.results[i];
if ("name" in result &&
"description" in result &&
"group" in result &&
"urlname" in result.group
) {
var groupOptions = {
url: "https://api.meetup.com/" + result.group.urlname + "?sign=true&photo-host=public&key=****",
json: true
};
var groupBody = await request(groupOptions);
if ("category" in groupBody && "name" in groupBody.category) {
var event = {
name: result.name,
description: result.description,
category: groupBody.category.name
};
await ref.push(event);
}
}
}
res.status(200).send("processed events");
}
}
);
}
);
const functions=require(“firebase函数”);
const admin=require(“firebase管理员”);
admin.initializeApp();
const request=require(“请求承诺本机”);
exports.foo=functions.https.onRequest(
异步(请求、恢复)=>{
var ref=admin.database().ref(“/foo”);
变量选项={
url:“https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=**“,
json:true
};
等待请求(
选项,
异步(错误、响应、正文)=>{
如果(错误){
console.error(JSON.stringify(error));
res.status(500.end();
}else if(正文中的“结果”){
对于(var i=0;
"engines": {
"node": "8"
}
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const request = require("request-promise-native");
exports.foo = functions.https.onRequest(
async (req, res) => {
var ref = admin.database().ref("/foo");
var options = {
url: "https://api.meetup.com/2/open_events?sign=true&photo-host=public&lat=39.747988&lon=-104.994945&page=20&key=****",
json: true
};
await request(
options,
async (error, response, body) => {
if (error) {
console.error(JSON.stringify(error));
res.status(500).end();
} else if ("results" in body) {
for (var i = 0; i < body.results.length; i++) {
var result = body.results[i];
if ("name" in result &&
"description" in result &&
"group" in result &&
"urlname" in result.group
) {
var groupOptions = {
url: "https://api.meetup.com/" + result.group.urlname + "?sign=true&photo-host=public&key=****",
json: true
};
var groupBody = await request(groupOptions);
if ("category" in groupBody && "name" in groupBody.category) {
var event = {
name: result.name,
description: result.description,
category: groupBody.category.name
};
await ref.push(event);
}
}
}
res.status(200).send("processed events");
}
}
);
}
);