diff --git a/src/components/App.tsx b/src/components/App.tsx index 6b475711..4ada7452 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -149,40 +149,76 @@ export default function App() { }; }, [canvas.curTime, dispatchCanvas, trackManager]); - // This fetches track IDs based on the selected point IDs. useEffect(() => { console.debug("effect-selectedPointIds: ", trackManager, canvas.selectedPointIds); if (!trackManager) return; - if (canvas.selectedPointIds.size == 0) return; + if (canvas.selectedPointIds.size === 0) return; - // this fetches the entire lineage for each track const updateTracks = async () => { console.debug("updateTracks: ", canvas.selectedPointIds); - canvas.selectedPointIds.forEach(async (pointId) => { - if (canvas.fetchedPointIds.has(pointId)) return; + + // Store promises for fetching all tracks and lineages + const allTrackPromises: Promise[] = []; + + canvas.selectedPointIds.forEach((pointId) => { + if (canvas.fetchedPointIds.has(pointId)) return; // Skip already fetched + setNumLoadingTracks((n) => n + 1); canvas.fetchedPointIds.add(pointId); - const trackIds = await trackManager.fetchTrackIDsForPoint(pointId); - // TODO: points actually only belong to one track, so can get rid of the outer loop - trackIds.forEach(async (trackId) => { - if (canvas.fetchedRootTrackIds.has(trackId)) return; - canvas.fetchedRootTrackIds.add(trackId); - const [lineage, trackData] = await trackManager.fetchLineageForTrack(trackId); - lineage.forEach(async (relatedTrackId: number, index) => { - if (canvas.tracks.has(relatedTrackId)) return; - const [pos, ids] = await trackManager.fetchPointsForTrack(relatedTrackId); - // adding the track *in* the dispatcher creates issues with duplicate fetching - // but we refresh so the selected/loaded count is updated - canvas.addTrack(relatedTrackId, pos, ids, trackData[index]); - canvas.clearPointIndicesCache; - dispatchCanvas({ type: ActionType.REFRESH }); - }); + + const trackPromise = trackManager.fetchTrackIDsForPoint(pointId).then(async (trackIds) => { + // Use for...of for async operations while maintaining parallelism + const lineagePromises: Promise[] = []; + + for (const trackId of trackIds) { + if (canvas.fetchedRootTrackIds.has(trackId)) continue; + + canvas.fetchedRootTrackIds.add(trackId); + + const lineagePromise = trackManager + .fetchLineageForTrack(trackId) + .then(async ([lineage, trackData]) => { + const relatedTrackPromises: Promise[] = []; + + for (const [index, relatedTrackId] of lineage.entries()) { + if (canvas.tracks.has(relatedTrackId)) continue; + + const pointsPromise = trackManager + .fetchPointsForTrack(relatedTrackId) + .then(([pos, ids]) => { + canvas.addTrack(relatedTrackId, pos, ids, trackData[index]); + dispatchCanvas({ type: ActionType.REFRESH }); + }); + + relatedTrackPromises.push(pointsPromise); + } + + // Wait for all related tracks to be fetched and rendered in parallel + await Promise.allSettled(relatedTrackPromises); + }); + + lineagePromises.push(lineagePromise); + } + + // Wait for all lineages to be fetched and processed in parallel + await Promise.allSettled(lineagePromises); + }); + + // Add the track fetching promise to the promises array + allTrackPromises.push(trackPromise); + + // Decrement the loading count once fetching is complete + trackPromise.finally(() => { + setNumLoadingTracks((n) => n - 1); }); - setNumLoadingTracks((n) => n - 1); }); + + // Wait for all tracks and lineages to be fetched in parallel + await Promise.allSettled(allTrackPromises); + console.log("All tracks have been rendered on the canvas"); }; + updateTracks(); - // TODO: add missing dependencies }, [trackManager, dispatchCanvas, canvas.selectedPointIds]); // playback time points