我正在尝试从另一个异步函数运行的循环中调用一个异步函数。这些函数调用API,而我正在使用request-promise
使用nodeJS
。
functions.js文件
const rp = require("request-promise");
// function (1)
async email_views: emailId => {
let data = {};
await rp({
url: 'myapiurl',
qs: { accessToken: 'xyz', emailID: emailId },
method: 'GET'
})
.then( body => { data = JSON.parse(body) })
.catch( error => { console.log(error} );
return data;
};
上面的JSON看起来像这样:
...
data:{
records: [
{
...
contactID: 123456,
...
},
{
...
contactID: 456789,
...
}
]
}
...
我正在运行一个循环以获取个人记录,并在其中获得与每个记录相关联的contactID
。
// function#2 (also in functions.js file)
async contact_detail: contactId => {
let data = {};
await rp({
url: 'myapiurl2',
qs: { accessToken: 'xyz', contactID: contactId },
method: 'GET'
})
.then( body => { data = JSON.parse(body) })
.catch( error => { console.log(error} );
return data;
};
上面的函数以一个contactId
作为参数,并获取调用另一个API端点的联系人的详细信息。
分别调用两个函数时,它们都能正常工作。但我正在尝试在这样的循环中完成此操作:
...
const result = await email_views(99999); // function#1
const records = result.data.records;
...
let names = "";
for( let i=0; i<records.length; i++) {
...
const cId = records[i].contactID;
const contact = await contact_detail(cId); // function#2
names += contact.data.firstName + " " + contact.data.lastName + " ";
...
}
console.log(names);
...
问题是我只能从上面的代码块中获得第一个联系人,即,即使我从function#1
中获得20条记录,也要在调用{{1 }}(函数#2)对于每个contact_detail
,我都会获得一次联系方式,即仅对第一个contactID (cId)
。休息,我什么也没得到!
使用cId
实现此目标的正确方法是什么?
更新:
nodeJs
我正在使用const { App } = require("jovo-framework");
const { Alexa } = require("jovo-platform-alexa");
const { GoogleAssistant } = require("jovo-platform-googleassistant");
const { JovoDebugger } = require("jovo-plugin-debugger");
const { FileDb } = require("jovo-db-filedb");
const custom = require("./functions");
const menuop = require("./menu");
const stateus = require("./stateus");
const alexaSpeeches = require("./default_speech");
const app = new App();
app.use(new Alexa(), new GoogleAssistant(), new JovoDebugger(), new FileDb());
let sp = "";
async EmailViewsByContactIntent() {
try {
const viewEmailId =
this.$session.$data.viewEmailIdSessionKey != null
? this.$session.$data.viewEmailIdSessionKey
: this.$inputs.view_email_Id_Number.value;
let pageIndex =
this.$session.$data.viewEmailPageIndex != null
? this.$session.$data.viewEmailPageIndex
: 1;
const result = await custom.email_views_by_emailId(
viewEmailId,
pageIndex
);
const records = result.data.records;
if (records.length > 0) {
const totalRecords = result.data.paging.totalRecords;
this.$session.$data.viewEmailTotalPages = totalRecords;
sp = `i have found a total of ${totalRecords} following view records. `;
if (totalRecords > 5) {
sp += `i will tell you 5 records at a time. for next 5 records, please say, next. `;
this.$session.$data.viewEmailIdSessionKey = this.$inputs.view_email_Id_Number.value;
this.$session.$data.viewEmailPageIndex++;
}
for (let i = 0; i < records.length; i++) {
const r = records[i];
/* Here I want to pass r.contactID as contactId in the function contact_detail like this: */
const contact = await custom.contact_detail(r.contactID);
const contact_name = contact.data.firstName + " " + contact.data.lastName;
/* The above two lines of code fetch contact_name for the first r.contactID and for the rest I get an empty string only. */
const formatted_date = r.date.split(" ")[0];
sp += `contact ID ${spellOut_speech_builder(
r.contactID
)} had viewed on ${formatted_date} from IP address ${
r.ipAddress
}. name of contact is, ${contact_name}. `;
}
if (totalRecords > 5) {
sp += ` please say, next, for next 5 records. `;
}
} else {
sp = ``;
}
this.ask(sp);
} catch (e) {
this.tell(e);
}
}
框架和JOVO
建立一种Alexa技能。
更新#2
作为测试,我只返回了传递给contact_detail函数的nodeJS
,并在我的第一个 UPDATE 下将正确的值返回到上述代码。
contactId
似乎即使正确地获得了值,该函数仍无法执行。但是,当我从另一个地方调用它时,相同的async contact_detail: contactId => {
return contactId;
}
函数也可以正常工作。仅在循环内不起作用。
可能是什么原因? 我一定想念什么,但是不知道!!
答案 0 :(得分:0)
您将异步等待和承诺混合在一起,这导致您感到困惑。通常,您会在给定位置使用另一种(因为异步等待有效地提供了语法糖,因此您可以避免处理冗长的Promise代码)。
因为您将两者混在一起,所以您所在的区域很奇怪,因此行为难以确定。
如果您想使用async await,您的功能应该看起来像
{
ids: ['1','2'],
entities: {
'1': {
...ObjectName
}
}
}
或有诺言
async contact_detail: contactId => {
try {
const body = await rp({
url: 'myapiurl2',
qs: { ... }
});
return JSON.parse(body);
} catch(e) {
console.log(e);
//This will return undefined in exception cases. You may want to catch at a higher level.
}
};
请记住,当前执行该函数的代码将依次执行每个调用。如果要并行执行这些操作,则需要以不同的方式调用该函数,并使用Promise.all之类的东西来解析结果。
答案 1 :(得分:0)
您在这里:
...
const result = await email_views(99999); // function#1
const records = result.data.records;
...
let names = "";
await Promise.all(records.map(async record => {
let cId = record.contactID;
let contact = await contact_detail(cId);
names += contact.data.firstName + " " + contact.data.lastName + " ";
});
console.log(names);
...
答案 2 :(得分:0)
我之所以将其发布为答案只是因为我需要向您展示一些多行代码,作为解决此问题的一部分。尚不确定这是否可以解决您的问题,但这是一个问题。
您的contact_detail()
函数未正确返回错误。而是吃掉错误并使用空对象解决。那可能是导致您的空白名称的原因。它应该直接返回promise,如果您想记录错误,则需要重新抛出它。另外,也没有理由将其声明为async
或使用await
。您可以直接退还承诺。您还可以让请求承诺也为您分配JSON响应。
我还注意到,您的.catch()
中似乎存在语法错误,这也可能是问题的一部分。
contact_detail: contactId => {
return rp({
url: 'myapiurl2',
qs: { accessToken: 'xyz', contactID: contactId },
json: true,
method: 'GET'
}).catch( error => {
// log error and rethrow so any error propagates
console.log(error);
throw error;
});
};
然后,您将像原来一样调用它(请注意,调用它时仍会使用await
,因为它会返回一个承诺):
...
const result = await email_views(99999); // function#1
const records = result.data.records;
...
let names = "";
for( let i=0; i<records.length; i++) {
...
const cId = records[i].contactID;
const contact = await contact_detail(cId);
names += contact.data.firstName + " " + contact.data.lastName + " ";
...
}
console.log(names);
...