memory-utils.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // Memory leak prevention utilities
  2. class RequestTracker {
  3. private activeRequests = new Set<AbortController>();
  4. addRequest(controller: AbortController) {
  5. this.activeRequests.add(controller);
  6. }
  7. removeRequest(controller: AbortController) {
  8. this.activeRequests.delete(controller);
  9. }
  10. cancelAllRequests() {
  11. this.activeRequests.forEach(controller => {
  12. try {
  13. controller.abort();
  14. } catch (error) {
  15. console.warn('Failed to abort request:', error);
  16. }
  17. });
  18. this.activeRequests.clear();
  19. }
  20. getActiveRequestCount(): number {
  21. return this.activeRequests.size;
  22. }
  23. }
  24. export const requestTracker = new RequestTracker();
  25. // Enhanced fetch with automatic tracking
  26. export function trackedFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
  27. const controller = new AbortController();
  28. const timeoutId = setTimeout(() => {
  29. controller.abort();
  30. }, 30000); // 30 second timeout
  31. // Merge abort signals
  32. if (init?.signal) {
  33. init.signal.addEventListener('abort', () => {
  34. controller.abort();
  35. });
  36. }
  37. const trackedInit = {
  38. ...init,
  39. signal: controller.signal,
  40. };
  41. requestTracker.addRequest(controller);
  42. return fetch(input, trackedInit).finally(() => {
  43. clearTimeout(timeoutId);
  44. requestTracker.removeRequest(controller);
  45. });
  46. }
  47. // Cleanup utility for components
  48. export function useCleanup() {
  49. const cleanupFunctions = new Set<() => void>();
  50. const addCleanup = (cleanup: () => void) => {
  51. cleanupFunctions.add(cleanup);
  52. };
  53. const executeCleanup = () => {
  54. cleanupFunctions.forEach(cleanup => {
  55. try {
  56. cleanup();
  57. } catch (error) {
  58. console.warn('Cleanup function failed:', error);
  59. }
  60. });
  61. cleanupFunctions.clear();
  62. };
  63. return { addCleanup, executeCleanup };
  64. }
  65. // Memory monitoring for development
  66. export function startMemoryMonitoring() {
  67. if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined') {
  68. setInterval(() => {
  69. if ('memory' in performance) {
  70. const memory = (performance as Performance & { memory?: { usedJSHeapSize: number; totalJSHeapSize: number; jsHeapSizeLimit: number } }).memory;
  71. if (!memory) return;
  72. const usedMB = Math.round(memory.usedJSHeapSize / 1048576);
  73. const limitMB = Math.round(memory.jsHeapSizeLimit / 1048576);
  74. // Log memory usage every 30 seconds
  75. if (usedMB > limitMB * 0.9) {
  76. console.warn(`High memory usage: ${usedMB}MB / ${limitMB}MB`);
  77. }
  78. }
  79. }, 30000);
  80. }
  81. }
  82. // Debounced function with cleanup
  83. export function debounceWithCleanup<T extends (...args: unknown[]) => unknown>(
  84. func: T,
  85. wait: number
  86. ): { debouncedFn: (...args: Parameters<T>) => void; cancel: () => void } {
  87. let timeoutId: NodeJS.Timeout | null = null;
  88. const debouncedFn = (...args: Parameters<T>) => {
  89. if (timeoutId) {
  90. clearTimeout(timeoutId);
  91. }
  92. timeoutId = setTimeout(() => {
  93. func(...args);
  94. timeoutId = null;
  95. }, wait);
  96. };
  97. const cancel = () => {
  98. if (timeoutId) {
  99. clearTimeout(timeoutId);
  100. timeoutId = null;
  101. }
  102. };
  103. return { debouncedFn, cancel };
  104. }