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