@robrogers3 thanks for that :-) I was trying to keep my question brief so skipped quite a lot of detail, but that's always a double-edged sword ;-)
It's a little DIY 'live' web front end to a torrent daemon running on a raspberry pi. It shows all the downloaded torrents, their current status (ETA, size etc). You can select them to be copied to another machine. You can also do a global refresh of the list which picks up newly added torrents (this is quite an 'expensive' operation so I'm not making automatic - can be upto 20seconds to get the list from the daemon).
Each torrent entry is responsible for updating its own state if it's still downloading or is being copied. The parent handles the global refresh of the list and kicking off the copying.
There are several thousand child components on the page (it's kind of like a big directory listing), so when they are first created they check 'am I still actively doing something? If yes, then setTimeout() to refresh myself' - and that setTimeout re-checks and kicks off another setTimeout if needed. So children who are 'finished' don't do anything which keeps the amount of background 'stuff' down.
That's where I think the race is occurring. The child makes a setTimeout ajax request which is going to return that it no longer needs to update. While that is happening the parent sets the flag saying 'yes, you need to update as you're being copied' - which is immediately replaced with the ajax result saying 'ah, no you don't'.
I originally had the parent be in charge of all the state - but it got really messy keeping track of all the timeout's etc.
I'm now experimenting with making the parent emit an event which each child listens for - but I'm a little wary of having so many event listeners active on the page.
Anyway - thanks for your reply - it's given me things to think about :-) It's just a little 'xmas holiday project' which has gotten a bit out of hand - so if it isn't perfect it doesn't really matter. But I'll keep plugging away at it :-) Thanks again! :-)