Performance Optimization
Strategies and patterns for optimal FitFileViewer performance.
Loading Performanceβ
Lazy Loadingβ
Modules are loaded on-demand:
// Only load map when needed
async function showMap() {
const { renderMap } = await import('./utils/maps/renderMap.js');
renderMap(container, data);
}
Code Splittingβ
Heavy libraries are split:
// Chart.js loaded only when charts tab is active
const Chart = await import('chart.js');
Rendering Performanceβ
Virtual Scrollingβ
For large data tables:
// DataTables handles pagination
$('#dataTable').DataTable({
serverSide: false,
deferRender: true, // Render visible rows only
pageLength: 50
});
Debouncingβ
User inputs are debounced:
// Debounce search input
const debouncedSearch = debounce((term) => {
filterTable(term);
}, 300);
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
RequestAnimationFrameβ
Smooth animations:
function animateChart() {
requestAnimationFrame(() => {
chart.update('none'); // No animation
});
}
Memory Managementβ
Cleanupβ
Components clean up resources:
class ChartManager {
constructor() {
this.charts = [];
}
createChart(config) {
const chart = new Chart(config);
this.charts.push(chart);
return chart;
}
cleanup() {
this.charts.forEach(chart => chart.destroy());
this.charts = [];
}
}
Event Listener Managementβ
// Track listeners for cleanup
const listeners = new Map();
function addListener(element, event, handler) {
element.addEventListener(event, handler);
if (!listeners.has(element)) {
listeners.set(element, []);
}
listeners.get(element).push({ event, handler });
}
function cleanup() {
listeners.forEach((handlers, element) => {
handlers.forEach(({ event, handler }) => {
element.removeEventListener(event, handler);
});
});
listeners.clear();
}
Large File Handlingβ
Streaming Parseβ
For large FIT files:
// Process in chunks
async function parseInChunks(buffer, chunkSize = 1000) {
const records = [];
let offset = 0;
while (offset < buffer.byteLength) {
const chunk = await parseChunk(buffer, offset, chunkSize);
records.push(...chunk);
offset += chunkSize;
// Yield to UI
await new Promise(resolve => setTimeout(resolve, 0));
}
return records;
}
Progressive Renderingβ
Show data as it's available:
async function loadAndRender(buffer) {
// Show loading state
showLoadingIndicator();
// Parse metadata first (fast)
const metadata = parseMetadata(buffer);
renderSummary(metadata);
// Then parse full data
const fullData = await parseFullData(buffer);
renderAllViews(fullData);
hideLoadingIndicator();
}
Map Performanceβ
Tile Cachingβ
Map tiles are cached:
// Leaflet handles caching automatically
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
// Tiles cached in browser
});
Route Simplificationβ
Simplify routes for display:
// Reduce points for overview
function simplifyRoute(points, tolerance = 0.0001) {
// Douglas-Peucker algorithm
return simplify(points, tolerance);
}
Metricsβ
Performance Monitoringβ
// Measure operation time
function measurePerformance(name, fn) {
const start = performance.now();
const result = fn();
const end = performance.now();
console.log(`${name}: ${(end - start).toFixed(2)}ms`);
return result;
}
// Usage
const data = measurePerformance('Parse FIT file', () => {
return parseFitFile(buffer);
});
Best Practicesβ
Doβ
- β Lazy load heavy modules
- β Debounce user input
- β Clean up resources
- β Use pagination
- β Show loading states
Don'tβ
- β Load everything upfront
- β Process on main thread
- β Keep unused references
- β Render thousands of DOM nodes
- β Block UI during processing
Related: Architecture Overview