nodeJS:如何在另一个异步函数调用

时间:2019-07-15 04:40:49

标签: node.js async-await request-promise

我正在尝试从另一个异步函数运行的循环中调用一个异步函数。这些函数调用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; } 函数也可以正常工作。仅在循环内不起作用。

可能是什么原因? 我一定想念什么,但是不知道!!

3 个答案:

答案 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);
...