Javascript 如何正确地从Array.map()函数返回异步数据
我正在开发一个小的web应用程序来跟踪本地棋盘游戏组的积分。记分板将记录玩家玩过的游戏总数、获得的总积分以及玩过的游戏的细分以及游戏中每个派系获得的积分 我目前正在尝试为/players/withparties/创建node/express端点,该端点将返回聚合玩家数据以及每个派系的细分 正如您将在所附的代码中看到的,逻辑工作正常,并且数据在第一个console.log的位置正确。该数据只是返回,我在第二个控制台日志中记录返回,在那里数据似乎已经更改。变异后的数据似乎总是采用从第一个console.log位置返回的最后一个元素的形式。我希望在审查代码后,这会更有意义 我确信我的问题在于如何处理所有的异步,但我无法找到解决方案 我用多种不同的方式来解决这个问题:回调、承诺、异步/等待。结果都一样。在返回前端之前,我的数据似乎发生了变化Javascript 如何正确地从Array.map()函数返回异步数据,javascript,node.js,postgresql,async-await,Javascript,Node.js,Postgresql,Async Await,我正在开发一个小的web应用程序来跟踪本地棋盘游戏组的积分。记分板将记录玩家玩过的游戏总数、获得的总积分以及玩过的游戏的细分以及游戏中每个派系获得的积分 我目前正在尝试为/players/withparties/创建node/express端点,该端点将返回聚合玩家数据以及每个派系的细分 正如您将在所附的代码中看到的,逻辑工作正常,并且数据在第一个console.log的位置正确。该数据只是返回,我在第二个控制台日志中记录返回,在那里数据似乎已经更改。变异后的数据似乎总是采用从第一个consol
router.get('/withFactions', async (req, res) => {
const { rows: players } = await db.query('SELECT * FROM players');
const { rows: factions } = await db.query('SELECT * FROM factions');
const playerResults = await Promise.all(players.map( async (player) => {
await db.query('SELECT * FROM gameentry WHERE player_id = $1', [player.id]).then((result) => {
const gameEntries = result.rows;
const factionTotals = factions.map((faction) => {
const factionEntries = gameEntries.filter(game => game.faction_id === faction.id)
faction.totalPoints = factionEntries.reduce((acc, curr) => {
return acc + curr.points;
}, 0)
return faction;
})
player.factionTotals = factionTotals;
})
player.factionTotals.forEach(element => console.log(element.totalPoints))
// total scores are recording correctly here 4,0,0 5,0,3 0,5,0 (from DB data)
return await player;
}))
await playerResults.forEach(player => player.factionTotals.forEach(element => console.log(element.totalPoints)));
// total scores are wrong here. Each set is 0,5,0. Seems to just replicate the last set from within the promise
res.send(await playerResults);
})
注意:唯一不正确的数据是玩家。其他玩家数据保持准确
我敢肯定,还有一些等待,而不是必要的。我研究这个问题太久了。我希望我能在这方面得到一些帮助。正如问题所指出的那样,你为每个玩家修改派系,所以只有最后一次修改才会“保留”
一般来说,我建议在使用map时不要有副作用,只返回新对象或未修改的对象。为此,您应该能够将代码修改为以下内容:
router.get('/withFactions', async (req, res) =>
{
const { rows: players } = await db.query('SELECT * FROM players');
const { rows: factions } = await db.query('SELECT * FROM factions');
const playerResults = await Promise.all(players.map(async player =>
{
const { rows: gameEntries } = await db.query('SELECT * FROM gameentry WHERE player_id = $1', [player.id]);
const factionTotals = factions.map(faction =>
{
const factionEntries = gameEntries.filter(game => game.faction_id === faction.id)
var totalPoints = factionEntries.reduce((acc, curr) => acc + curr.points, 0);
return { ...faction, totalPoints }; // Copies faction properties instead of modifying
})
const extendedPlayer = { ...player, factionTotals };
extendedPlayer.factionTotals.forEach(element => console.log(element.totalPoints))
return extendedPlayer;
}))
playerResults.forEach(player => player.factionTotals.forEach(element => console.log(element.totalPoints)));
res.send(playerResults);
})
使用排列(…
)复制属性,因此原始派系
列表对于每个玩家迭代都是相同的。创建extendedPlayer
的第二个排列可能不是严格必要的,以防止您的问题,但也保持外部map
的纯净。顺便说一句,为了使代码更可读,您不需要在return wait player
或wait playerResults.forEach
或res.send中等待(等待playerResults);
-因为这些都不是可以独立运行的最小示例(例如,仅使用示例数组承诺而不是DB查询)会有帮助
每一次,所以最终结果当然将是玩家.map的最终迭代结果。
…尝试const派系总数=派系.slice().map((派系)=>{
@JaromandaX:这似乎是个问题,不妨将其作为一个答案发布。这表明在地图中修改状态通常不是一个好主意…@JaromandaX:切片可能没有帮助,这只是一个肤浅的复制,派系对象仍然是一样的。这比切片好得多:p@JaromandaX:谢谢,我希望你能你不介意我回答这个问题。(我以为你现在已经发布了答案,所以我继续了。)非常感谢你的帮助!成功了。非常感谢你的支持!