如何正确地从 Array.map() 函数返回异步数据



我正在开发一个小型网络应用程序来跟踪本地棋盘游戏组的积分。记分牌将跟踪玩家玩过的游戏总数、获得的总积分以及游戏中每个派系的比赛明细和获得的积分。

我目前正在尝试为/players/withFactions/创建节点/快速端点,这将返回聚合玩家数据以及每个派系细分。

正如您将在附加的代码中看到的那样,逻辑工作正常,数据在第一个控制台的位置是正确的.log。该数据只是返回,我在第二个控制台日志中记录返回,其中数据似乎已更改。突变的数据似乎总是采用第一个控制台位置返回的最后一个元素的形式.log。我希望这在审查代码时会更有意义。

我确信我的问题在于我如何处理所有异步,但我无法找出解决方案。

我已经以多种不同的方式解决了这个问题:回调,承诺,异步/等待。结果都是一样的。数据似乎在返回到前端之前发生了变化。

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);
})

注意:唯一不正确的数据是 player.factionTotals 其余的玩家数据保持准确。

我相信那里还有比必要的更多的等待。我一直在研究这个问题太久了。我希望我能在这个问题上得到一些帮助。

正如Jaromanda X所确定的那样,问题是你修改了每个玩家的factions,所以只有最后的修改会"坚持下去"。

一般来说,我建议在使用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);
})

使用点差(...(复制属性,因此原始factions列表对于每个玩家迭代都是相同的。用于创建extendedPlayer的第二个传播可能不是防止您的问题的绝对必要条件,但也可以保持外部map的纯净。

最新更新