【问题标题】:JS Array map returning emptyJS 数组映射返回空
【发布时间】:2020-04-20 04:33:19
【问题描述】:

我正在尝试使用 map 在对象数组中返回一个新字段,一旦我这样做,它就会返回空。

预期结果:

[
    {
        "id": 2,
        "name": "awesome stadium",
        "opened": "1970-01-01T00:00:00.000Z",
        "capacity": "39590.00",
        "location": "some awesome",
        "team_id": "147ff48e",
        "team": {
            "name": "some name",
            "location": "some location"
        }
    }
]

代码:

    const venues = await connection(VENUE_TABLE)
      .limit(LIMIT_PER_PAGE)
      .offset((page - 1) * LIMIT_PER_PAGE)
      .select('*');

    const venuesWithTeam = venues.map(async (element) => ({
      ...element,
      team: await connection('team')
        .where('id', element.team_id)
        .select('*')
        .first(),
    }));

    return response.json(venuesWithTeam);

我该怎么做才能让它按预期工作?

【问题讨论】:

  • venuesWithTeam 将是一个 Promise 数组 - 它不是空的 - 它只是空的,因为您正在尝试将 Promise 转换为 JSON - 这不会产生任何结果
  • 试试const venuesWithTeam = await Promise.all(venues.map .... etc)

标签: javascript node.js express async-await knex.js


【解决方案1】:

async 函数总是返回一个 Promise,但 .map 不会“等待”这些承诺(它只是将它们“原样”放在结果中)。如果你想得到所有的结果,最简单的方法是Promise.all:

const venuesWithTeam = await Promise.all(venues.map(async (element) => ({
  ...element,
  team: await connection('team')
    .where('id', element.team_id)
    .select('*')
    .first(),
})));

注意:这将开始并行(同时)运行所有操作,并等待所有操作都解决后再继续。如果venues 是一个非常大的数组,它可能会导致大量连接到您的数据库并可能导致一些问题。

要一个接一个地运行请求,您必须循环并等待前一个请求得到解决。一种方法是使用reduce

const venuesWithTeam = await venues.reduce((prom, element) => (
  prom.then(async result => (
    result.concat({
      ...element,
      team: await connection('team')
        .where('id', element.team_id)
        .select('*')
        .first(),
    })
  ))
), Promise.resolve([]));

【讨论】:

  • 非常感谢,它对我有用。由于第一个并行运行操作,我更喜欢使用第二个。
  • 我一直忘记地图不能与我一直忘记的等待的东西
【解决方案2】:

venues.map 将返回一个 Promises 数组。 Promise 不会字符串化为 JSON - 嗯,它们会字符串化到 {}

因此,您必须在代码中等待 .map 返回的所有承诺

所以,Promise.all(x.map( ..... ))

const venuesWithTeam = await Promise.all(venues.map(async(element) => ({
  ...element,
  team: await connection('team')
    .where('id', element.team_id)
    .select('*')
    .first(),
})));

return response.json(venuesWithTeam);

或者,以串行方式而不是并行方式运行每次迭代

const venuesWithTeam = [];
for (element of venues) {
  venuesWithTeam.push({
    ...element,
    team: await connection('team')
      .where('id', element.team_id)
      .select('*')
      .first(),
  });
}
return response.json(venuesWithTeam);

【讨论】:

    【解决方案3】:

    您需要在代码中使用Promise.All

    const venuesWithTeam = await Promise.all(venues.map(async (element) => ({
      ...element,
      team: await connection('team')
        .where('id', element.team_id)
        .select('*')
        .first(),
    })));
    

    【讨论】:

    • 你让venuesWithTeam 成为一个返回Promise 的函数——这和问题一样糟糕
    • @JaromandaX 哦,是的。我错过了。我已经编辑过了。请立即检查。
    • 是的,这是我在评论中建议的代码,并作为答案发布
    猜你喜欢
    • 2012-05-17
    • 2019-10-31
    • 2019-12-05
    • 2019-10-22
    • 1970-01-01
    • 2018-05-30
    • 2012-10-10
    • 2021-02-17
    • 2019-01-05
    相关资源
    最近更新 更多