Manage multiple timer tasks with a single SharedWebWorker. Perfect for handling recurring tasks like token refresh, inactivity detection, and sleep mode across your application.
- ✅ Single SharedWebWorker - All timers run in one worker, reducing overhead
- ✅ Named Task Management - Easy to manage with unique task identifiers
- ✅ Repeating & One-time Tasks - Flexible timer control
- ✅ Cross-tab Synchronization - Shared timers across browser tabs
- ✅ TypeScript Support - Full type definitions included
- ✅ Zero Dependencies - Lightweight and fast
- ✅ Framework Agnostic - Works with React, Vue, Angular, Svelte, and vanilla JS
Using a SharedWorker for scheduling time-sensitive tasks is often preferable to relying on setInterval/setTimeout on the main thread:
- Runs off the main thread: timers in a
SharedWorkerare not blocked by main-thread work (UI rendering, long-running scripts), which reduces jitter and missed ticks. - Less affected by event-loop blocking: main-thread timers pause or drift when the event loop is busy; a worker provides an isolated execution context so scheduled tasks execute more reliably.
- Cross-tab single source of truth: a
SharedWorkercan serve multiple tabs/windows, avoiding duplicated timers and reducing cumulative drift when the same task would otherwise run in many contexts. - Resource-efficient: centralizing timers avoids redundant timers across contexts and reduces overall CPU usage.
- Background behavior: while browsers may throttle background activity, a
SharedWorkercentralizes coordination so connectivity-dependent tasks (e.g., token refresh) remain consistent across open pages.
npm install shared-timer-managercp node_modules/shared-timer-manager/dist/shared-timer-worker.js public/import TimerManager from 'shared-timer-manager';
// Initialize
const timerManager = new TimerManager('/shared-timer-worker.js');
// Add a repeating task
timerManager.addTimerTask(
'token-refresh',
15 * 60 * 1000, // 15 minutes
() => {
console.log('Refreshing token...');
refreshToken();
},
true // repeats
);
// Add a one-time task
timerManager.addTimerTask(
'inactivity-logout',
5 * 60 * 1000, // 5 minutes
() => {
console.log('Logging out due to inactivity');
logout();
},
false // doesn't repeat
);
// Clear a specific task
timerManager.clearTimerTask('token-refresh');
// Remove all tasks
timerManager.removeAllTimerTasks();Creates a new TimerManager instance.
Parameters:
workerPath(string, optional): Path to the shared worker file. Default:'/shared-timer-worker.js'
Example:
const timerManager = new TimerManager('/workers/shared-timer-worker.js');Adds a new timer task.
Parameters:
taskName(string): Unique identifier for the taskintervalTime(number): Interval in millisecondscallback(function): Function to execute on each interval. Receives(taskName, timestamp)as argumentscanRepeat(boolean, optional): Whether the task should repeat. Default:true
Example:
timerManager.addTimerTask('my-task', 5000, (name, timestamp) => {
console.log(`${name} executed at ${timestamp}`);
}, true);Clears a specific timer task.
Parameters:
taskName(string): Name of the task to clear
Example:
timerManager.clearTimerTask('my-task');Removes all timer tasks.
Example:
timerManager.removeAllTimerTasks();Check if a task exists.
Parameters:
taskName(string): Name of the task
Returns:
boolean: Whether the task exists
Example:
if (timerManager.hasTask('token-refresh')) {
console.log('Token refresh task is active');
}Get list of active task names.
Returns:
Array<string>: Array of active task names
Example:
const tasks = timerManager.getActiveTasks();
console.log('Active tasks:', tasks); // ['token-refresh', 'inactivity-logout']Check if the worker is connected (health check).
Parameters:
taskName(string, optional): Task name for ping. Default:'health-check'
Returns:
boolean: Connection status
Example:
const isConnected = timerManager.ping();Automatically refresh authentication tokens at regular intervals:
timerManager.addTimerTask(
'token-refresh',
15 * 60 * 1000, // Every 15 minutes
async () => {
try {
await refreshAccessToken();
console.log('Token refreshed successfully');
} catch (error) {
console.error('Token refresh failed:', error);
}
},
true
);Log out users after a period of inactivity:
function setupInactivityTimer() {
timerManager.addTimerTask(
'inactivity-logout',
5 * 60 * 1000, // 5 minutes
() => {
console.log('User inactive - logging out');
logout();
},
false
);
}
// Reset timer on user activity
const resetTimer = () => {
timerManager.clearTimerTask('inactivity-logout');
setupInactivityTimer();
};
document.addEventListener('mousemove', resetTimer);
document.addEventListener('keydown', resetTimer);
document.addEventListener('click', resetTimer);
// Initialize
setupInactivityTimer();Reduce backend requests after inactivity:
function setupSleepMode() {
timerManager.addTimerTask(
'sleep-mode',
5 * 60 * 1000, // 5 minutes
() => {
console.log('Activating sleep mode');
activateSleepMode();
// Stop non-critical API polling
stopBackgroundSync();
},
false
);
}
// Reset on user activity
const resetSleepMode = () => {
if (timerManager.hasTask('sleep-mode')) {
timerManager.clearTimerTask('sleep-mode');
deactivateSleepMode();
setupSleepMode();
}
};
window.addEventListener('focus', resetSleepMode);
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
resetSleepMode();
}
});
setupSleepMode();Create a custom hook for easy integration:
// hooks/useTimerManager.js
import { useEffect, useRef } from 'react';
import TimerManager from 'shared-timer-manager';
export function useTimerManager() {
const timerManagerRef = useRef(null);
useEffect(() => {
timerManagerRef.current = new TimerManager('/shared-timer-worker.js');
return () => {
timerManagerRef.current?.removeAllTimerTasks();
};
}, []);
return timerManagerRef.current;
}
// Usage in component
function App() {
const timerManager = useTimerManager();
useEffect(() => {
if (timerManager) {
timerManager.addTimerTask('token-refresh', 15 * 60 * 1000, () => {
refreshToken();
}, true);
}
}, [timerManager]);
return <div>My App</div>;
}// composables/useTimerManager.js
import { onUnmounted } from 'vue';
import TimerManager from 'shared-timer-manager';
const timerManager = new TimerManager('/shared-timer-worker.js');
export function useTimerManager() {
onUnmounted(() => {
timerManager.removeAllTimerTasks();
});
return {
addTask: (name, interval, callback, repeat = true) =>
timerManager.addTimerTask(name, interval, callback, repeat),
clearTask: (name) => timerManager.clearTimerTask(name),
removeAll: () => timerManager.removeAllTimerTasks(),
hasTask: (name) => timerManager.hasTask(name),
getActiveTasks: () => timerManager.getActiveTasks()
};
}
// Usage in component
<script setup>
import { onMounted } from 'vue';
import { useTimerManager } from '@/composables/useTimerManager';
const { addTask, clearTask } = useTimerManager();
onMounted(() => {
addTask('token-refresh', 15 * 60 * 1000, () => {
console.log('Refreshing token...');
});
});
</script>// services/timer-manager.service.ts
import { Injectable, OnDestroy } from '@angular/core';
import TimerManager from 'shared-timer-manager';
@Injectable({
providedIn: 'root'
})
export class TimerManagerService implements OnDestroy {
private timerManager: TimerManager;
constructor() {
this.timerManager = new TimerManager('/shared-timer-worker.js');
}
addTask(name: string, interval: number, callback: Function, repeat = true): void {
this.timerManager.addTimerTask(name, interval, callback as any, repeat);
}
clearTask(name: string): void {
this.timerManager.clearTimerTask(name);
}
removeAllTasks(): void {
this.timerManager.removeAllTimerTasks();
}
hasTask(name: string): boolean {
return this.timerManager.hasTask(name);
}
getActiveTasks(): string[] {
return this.timerManager.getActiveTasks();
}
ngOnDestroy(): void {
this.timerManager.removeAllTimerTasks();
}
}
// Usage in component
export class AppComponent {
constructor(private timerService: TimerManagerService) {
this.timerService.addTask('token-refresh', 15 * 60 * 1000, () => {
console.log('Refreshing token...');
}, true);
}
}// stores/timerStore.js
import { onDestroy } from 'svelte';
import TimerManager from 'shared-timer-manager';
const timerManager = new TimerManager('/shared-timer-worker.js');
export function useTimer() {
onDestroy(() => {
timerManager.removeAllTimerTasks();
});
return timerManager;
}
// Usage in component
<script>
import { onMount } from 'svelte';
import { useTimer } from './stores/timerStore';
const timer = useTimer();
onMount(() => {
timer.addTimerTask('my-task', 5000, () => {
console.log('Timer tick!');
}, true);
});
</script>import TimerManager from 'shared-timer-manager';
const timerManager = new TimerManager('/shared-timer-worker.js');
// Add tasks
timerManager.addTimerTask('periodic-sync', 30000, () => {
syncData();
}, true);
// Clear when needed
document.getElementById('stop-btn').addEventListener('click', () => {
timerManager.clearTimerTask('periodic-sync');
});SharedWebWorker is supported in:
- ✅ Chrome/Edge 4+
- ✅ Firefox 29+
- ✅ Safari 16+
- ❌ Internet Explorer (not supported)
Note: For browsers that don't support SharedWebWorker, consider implementing a fallback using regular setInterval or Web Workers.
This package includes TypeScript definitions. No additional types package needed!
import TimerManager from 'shared-timer-manager';
const timerManager: TimerManager = new TimerManager('/shared-timer-worker.js');
timerManager.addTimerTask(
'my-task',
5000,
(taskName: string, timestamp: number) => {
console.log(`Task ${taskName} executed at ${timestamp}`);
},
true
);Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT © [Dario Espina]
If you encounter any issues or have questions:
- Initial release
- Support for multiple timer tasks with single SharedWebWorker
- Cross-tab synchronization
- TypeScript support
- Framework-agnostic implementation