我试图从另一个异步函数运行的循环中调用一个异步函数。这些函数调用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条记录,在循环中,当我为每个contactID (cId)调用contact_detail (function#2)时,我只获得一次联系人详细信息,即仅针对第一个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框架和nodeJS构建alexa技能。
更新2作为一个测试,我只返回了传递给contact_detail函数的contactId,并在第一个更新下将正确的值返回到上面的代码。
async contact_detail: contactId => {
return contactId;
}似乎即使在得到正确的值之后,函数也不知何故无法执行。但是,当我从另一个地方调用它时,相同的contact_detail函数工作得很好。只是在循环中不起作用。
可能是什么原因?我一定是错过了什么,但我不知道是什么!
发布于 2019-07-15 05:04:02
您正在将异步等待和承诺混合在一起,这将导致您的困惑。您通常会在给定的位置使用另一种(异步等待有效地提供语法糖,这样您就可以避免处理冗长的承诺代码)。
因为你把两者混为一谈,所以你处在一个奇怪的领域,那里的行为很难确定。
如果您想使用异步等待,您的函数应该如下所示
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.
}
};或承诺
async contact_detail: contactId => {
return rp({
url: 'myapiurl2',
qs: { ... }
})
.then( body => JSON.parse(body))
.catch( error => {
console.log(error);
//This will return undefined in exception cases. You probably dont want to catch here.
});
};请记住,执行该函数的当前代码将按顺序执行每个调用。如果您想并行地执行这些操作,则需要对函数进行稍微不同的调用,并使用类似于Promise.all的方法来解析结果。
发布于 2019-07-15 05:12:31
给你:
...
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);
...发布于 2019-07-15 07:12:12
我将此作为一个答案发布,只是因为我需要向您展示一些多行代码,作为解决这个问题的一部分。不确定这是否解决了你的问题,但这是一个问题。
您的contact_detail()函数没有正确返回错误。相反,它会吃掉错误并使用空对象进行解析。这可能是导致你的名字空白的原因。它应该直接返回承诺,如果您想记录错误,那么它需要重新抛出。而且,没有理由将其声明为async或使用await。你可以直接回报你的承诺。您也可以让request承诺为您提供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);
...https://stackoverflow.com/questions/57033331
复制相似问题