| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- // Memory leak prevention utilities
- class RequestTracker {
- private activeRequests = new Set<AbortController>();
- addRequest(controller: AbortController) {
- this.activeRequests.add(controller);
- }
- removeRequest(controller: AbortController) {
- this.activeRequests.delete(controller);
- }
- cancelAllRequests() {
- this.activeRequests.forEach(controller => {
- try {
- controller.abort();
- } catch (error) {
- console.warn('Failed to abort request:', error);
- }
- });
- this.activeRequests.clear();
- }
- getActiveRequestCount(): number {
- return this.activeRequests.size;
- }
- }
- export const requestTracker = new RequestTracker();
- // Enhanced fetch with automatic tracking
- export function trackedFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => {
- controller.abort();
- }, 30000); // 30 second timeout
- // Merge abort signals
- if (init?.signal) {
- init.signal.addEventListener('abort', () => {
- controller.abort();
- });
- }
- const trackedInit = {
- ...init,
- signal: controller.signal,
- };
- requestTracker.addRequest(controller);
- return fetch(input, trackedInit).finally(() => {
- clearTimeout(timeoutId);
- requestTracker.removeRequest(controller);
- });
- }
- // Cleanup utility for components
- export function useCleanup() {
- const cleanupFunctions = new Set<() => void>();
- const addCleanup = (cleanup: () => void) => {
- cleanupFunctions.add(cleanup);
- };
- const executeCleanup = () => {
- cleanupFunctions.forEach(cleanup => {
- try {
- cleanup();
- } catch (error) {
- console.warn('Cleanup function failed:', error);
- }
- });
- cleanupFunctions.clear();
- };
- return { addCleanup, executeCleanup };
- }
- // Memory monitoring for development
- export function startMemoryMonitoring() {
- if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined') {
- setInterval(() => {
- if ('memory' in performance) {
- const memory = (performance as Performance & { memory?: { usedJSHeapSize: number; totalJSHeapSize: number; jsHeapSizeLimit: number } }).memory;
- if (!memory) return;
- const usedMB = Math.round(memory.usedJSHeapSize / 1048576);
- const limitMB = Math.round(memory.jsHeapSizeLimit / 1048576);
-
- // Log memory usage every 30 seconds
- if (usedMB > limitMB * 0.9) {
- console.warn(`High memory usage: ${usedMB}MB / ${limitMB}MB`);
- }
- }
- }, 30000);
- }
- }
- // Debounced function with cleanup
- export function debounceWithCleanup<T extends (...args: unknown[]) => unknown>(
- func: T,
- wait: number
- ): { debouncedFn: (...args: Parameters<T>) => void; cancel: () => void } {
- let timeoutId: NodeJS.Timeout | null = null;
- const debouncedFn = (...args: Parameters<T>) => {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
- timeoutId = setTimeout(() => {
- func(...args);
- timeoutId = null;
- }, wait);
- };
- const cancel = () => {
- if (timeoutId) {
- clearTimeout(timeoutId);
- timeoutId = null;
- }
- };
- return { debouncedFn, cancel };
- }
|