Background
The following program tracks events. For simplicity, each event has a bfId, baHome.id, baAway.id and it generates a performance object where each key is a period of periods and contains home and away, each containing scored and conceded fields (please see structure below)
Problem
I get a variable leak while instances of updatePerformance() are running in parallel. bfId changes while waiting for getPerformance() to resolve. I'm assuming it is a scope issue and I'm passing a reference which is changing somewhere, but it shouldn't. Any ideas on how to tackle this?
Failed attempts
I've tried passing a queryId (random number) to getPerformance() and returning it back to updatePerformance(). No errors there, but bfId changes within updatePerformance() after awaiting and it adds it to the wrong event. Why is this happening?
Global object format
// trackedEvents generates an object where keys are bfIds, in the following format:
{
'932481': {
baHome: { id: 1234 },
baAway: { id: 4911 },
performance: {
'5': { home: { scored: 9, conceded: 4 }, away: { scored: 1, conceded: 3 } },
'10': { home: { scored: 12, conceded: 7 }, away: { scored: 3, conceded: 9 } },
// ... '15', '20'
}
}
}
Code
Here's some simplified code:
const periods = [ 5, 10, 15, 20 ];
let trackedEvents = {};
;(async () => {
const bfIds = await getBfIds();
for(bfId of bfIds)
addEvent(bfId);
})();
async function addEvent(bfId) {
// Event is new, add to global object
trackedEvents[bfId] = {
baHome: { id: getHomeId(bfId) },
baAway: { id: getAwayId(bfId) }
}
await updatePerformance(bfId);
}
async function updatePerformance(bfId) {
const event = trackedEvents[bfId];
if(!event.performance)
trackedEvents[bfId].performance = {};
// Queueing performance periods
for(period of periods)
if(!event.performance[period])
performanceQueue.push(getPerformance(event.baHome.id, event.baAway.id, period));
// Wait for performance periods to resolve
const performance = await Promise.all(performanceQueue);
// Add performance is specified format // ATTENTION: bfId changes after await
performance.forEach((result) => {
trackedEvents[bfId].performance[result.period] = result;
});
}
// Returns all performance for period
async function getPerformance(baHomeId, baAwayId, period) {
const [ home, away ] = await Promise.all([
getLastStats(baHomeId, period), // Get home team performance
getLastStats(baAwayId, period) // Get away team performance
]);
return { home, away, period };
}
// Returns performance for home or away team
async function getLastStats(baId, period) {
// Fetch last "period" games from db where team played
const lastGames = await db.collection('events').find({
$or: [{ "baHome.id": baId }, { "baAway.id": baId }],
"score.home": { $exists: true }
}).sort({ openDate: -1 }).limit(period).toArray();
// Add scored and conceded goals
let scored = 0, conceded = 0;
for(game of lastGames) {
const [ homeScore, awayScore ] = [ parseInt(game.score.home), parseInt(game.score.away) ];
// Team playing as home team
if(baId == game.baHome.id) {
scored += homeScore;
conceded += awayScore;
}
// Team playing as away team
else {
scored += awayScore;
conceded += homeScore;
}
}
return { scored, conceded };
}