首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >nodeJS:如何在另一个异步函数调用中调用循环中的异步函数

nodeJS:如何在另一个异步函数调用中调用循环中的异步函数
EN

Stack Overflow用户
提问于 2019-07-15 04:40:49
回答 3查看 1.4K关注 0票数 0

我试图从另一个异步函数运行的循环中调用一个异步函数。这些函数调用API,我正在使用request-promise使用nodeJS

functions.js文件

代码语言:javascript
复制
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如下所示:

代码语言:javascript
复制
...
data:{
   records: [
      {
        ...
        contactID: 123456,
        ...
      },
      {
        ...
        contactID: 456789,
        ...
      }
   ]
}
...

我正在运行一个循环以获取单个记录,其中我将获得与每个记录相关联的contactID

代码语言:javascript
复制
// 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端点的详细信息。

当单独调用这两个函数时,它们都可以正常工作。但我正试图在这样的循环中这样做:

代码语言:javascript
复制
...
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实现这一目标的正确方法是什么?

更新:

代码语言:javascript
复制
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,并在第一个更新下将正确的值返回到上面的代码。

代码语言:javascript
复制
async contact_detail: contactId => {
   return contactId;
}

似乎即使在得到正确的值之后,函数也不知何故无法执行。但是,当我从另一个地方调用它时,相同的contact_detail函数工作得很好。只是在循环中不起作用。

可能是什么原因?我一定是错过了什么,但我不知道是什么!

EN

回答 3

Stack Overflow用户

发布于 2019-07-15 05:04:02

您正在将异步等待和承诺混合在一起,这将导致您的困惑。您通常会在给定的位置使用另一种(异步等待有效地提供语法糖,这样您就可以避免处理冗长的承诺代码)。

因为你把两者混为一谈,所以你处在一个奇怪的领域,那里的行为很难确定。

如果您想使用异步等待,您的函数应该如下所示

代码语言:javascript
复制
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.
  }
};

或承诺

代码语言:javascript
复制
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的方法来解析结果。

票数 0
EN

Stack Overflow用户

发布于 2019-07-15 05:12:31

给你:

代码语言:javascript
复制
...
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);
...
票数 0
EN

Stack Overflow用户

发布于 2019-07-15 07:12:12

我将此作为一个答案发布,只是因为我需要向您展示一些多行代码,作为解决这个问题的一部分。不确定这是否解决了你的问题,但这是一个问题。

您的contact_detail()函数没有正确返回错误。相反,它会吃掉错误并使用空对象进行解析。这可能是导致你的名字空白的原因。它应该直接返回承诺,如果您想记录错误,那么它需要重新抛出。而且,没有理由将其声明为async或使用await。你可以直接回报你的承诺。您也可以让request承诺为您提供JSON响应的部分。

另外,我注意到,您的.catch()中似乎有语法错误,这也可能是问题的一部分。

代码语言:javascript
复制
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,因为它返回一个承诺):

代码语言:javascript
复制
...
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);
...
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57033331

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档