From e5abeba11cd2e24cf01b6e42e2d642e670d3cfc5 Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 19 Mar 2026 21:49:45 +0000 Subject: [PATCH] refactor(satellite): simplify telemetry abort controller management Consolidated to a single active-request guard with cleanup in finally. The previous pattern had redundant null-checks across try and catch, and an always-false check on a controller that was already null. Cancel-on- new-request is now explicit before creating the new controller. --- templates/satellite_dashboard.html | 42 ++++++++++++++++++------------ 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/templates/satellite_dashboard.html b/templates/satellite_dashboard.html index e186686..8beda39 100644 --- a/templates/satellite_dashboard.html +++ b/templates/satellite_dashboard.html @@ -1373,17 +1373,22 @@ const lat = parseFloat(document.getElementById('obsLat')?.value); const lon = parseFloat(document.getElementById('obsLon')?.value); if (!Number.isFinite(lat) || !Number.isFinite(lon) || !selectedSatellite) return; - const requestKey = `telemetry:${requestedSatellite}:${lat.toFixed(3)}:${lon.toFixed(3)}`; - if (_telemetryAbortController && _activeTelemetryRequestKey === requestKey) { - return; + const requestKey = `telemetry:${requestedSatellite}:${lat.toFixed(3)}:${lon.toFixed(3)}`; + if (_activeTelemetryRequestKey === requestKey) return; // identical request already in flight + + // Cancel any in-flight request for a different satellite/location + if (_telemetryAbortController) { + _telemetryAbortController.abort(); + _telemetryAbortController = null; } + const controller = new AbortController(); + _telemetryAbortController = controller; + _activeTelemetryRequestKey = requestKey; + try { - const controller = new AbortController(); - _telemetryAbortController = controller; - _activeTelemetryRequestKey = requestKey; - const timeout = setTimeout(() => controller.abort(), TELEMETRY_FETCH_TIMEOUT_MS); + const timeoutId = setTimeout(() => controller.abort(), TELEMETRY_FETCH_TIMEOUT_MS); const response = await fetch('/satellite/position', { method: 'POST', credentials: 'same-origin', @@ -1396,25 +1401,28 @@ includeTrack: false }) }); - clearTimeout(timeout); - if (_telemetryAbortController === controller) { - _telemetryAbortController = null; - } - if (_activeTelemetryRequestKey === requestKey) { - _activeTelemetryRequestKey = null; - } + clearTimeout(timeoutId); + if (!response.ok) return; const contentType = response.headers.get('Content-Type') || ''; if (!contentType.includes('application/json')) return; const data = await response.json(); if (data.status !== 'success' || !Array.isArray(data.positions)) return; + + // Discard if satellite or selection changed while request was in flight + if (selectionToken !== _satelliteSelectionRequestToken || requestedSatellite !== selectedSatellite) return; + const pos = data.positions.find(p => parseInt(p.norad_id, 10) === requestedSatellite) || null; if (!pos) return; cacheLivePosition(requestedSatellite, pos); - if (selectionToken !== _satelliteSelectionRequestToken || requestedSatellite !== selectedSatellite) return; handleLivePositions(data.positions, 'poll'); - } catch (_) { - if (_telemetryAbortController?.signal?.aborted) { + + } catch (err) { + if (err?.name === 'AbortError') return; // expected on cancel/timeout + console.debug('Telemetry fetch error:', err); + } finally { + // Always release the controller slot so the next poll can run + if (_telemetryAbortController === controller) { _telemetryAbortController = null; } if (_activeTelemetryRequestKey === requestKey) {