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

时间:2019-08-11 02:19:06

标签: javascript node.js postgresql async-await

我正在开发一个小型Web应用程序,以跟踪本地棋盘游戏小组的积分。计分板将跟踪玩家玩过的游戏总数,获得的总积分,玩过的游戏的细目分类以及游戏中每个派系可获得的得分。

我目前正在撞墙,试图为/ players / withFactions /创建节点/表达端点,这将返回聚合的玩家数据以及每个派系的故障。

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

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

我已经以多种不同方式攻击了此问题:回调,promise,异步/等待。所有结果都相同。在返回前端之前,数据似乎在改变我。

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.factionTotal,其余的玩家数据仍保持准确。

我确信那里等待的次数比必要的多。我一直在研究这个问题太久了。希望我能对此有所帮助。

1 个答案:

答案 0 :(得分:1)

Jaromanda X所标识的问题是,您为每个玩家修改了factions,因此只有最后一次修改会“停留”。

通常,我建议使用地图时不要有副作用,而只返回新对象或未修改的对象。为此,您应该可以将代码修改为如下形式:

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保持纯净。