Chrome Extension Alarms API: Schedule Tasks and Periodic Events
The Chrome Extension Alarms API is one of the most powerful yet frequently overlooked APIs available to extension developers. When building Chrome extensions, you often need to execute code at specific times or at regular intervals—whether it’s syncing data from a server, sending periodic notifications, refreshing cached content, or performing background maintenance tasks. The Alarms API provides a robust solution for these time-based requirements that works reliably even when your extension’s service worker has been terminated.
This comprehensive guide will walk you through everything you need to know about implementing scheduled tasks and periodic events in your Chrome extensions using the Alarms API. We’ll cover the fundamental concepts, explore all available methods, provide practical code examples, and share best practices that will help you build reliable and efficient scheduled operations.
Understanding the Chrome Alarms API
The Chrome Alarms API, accessible through the chrome.alarms namespace, provides a mechanism for scheduling code execution at specific times or at regular intervals. Unlike traditional JavaScript timing functions like setTimeout and setInterval, which can be throttled by the browser when tabs are inactive, Chrome Alarms are designed to be reliable and persistent.
Why Use the Alarms API?
There are several compelling reasons to use the Alarms API in your Chrome extensions:
Reliability is the primary advantage of Chrome Alarms. The browser manages these alarms directly, which means they are not subject to the same throttling limitations that affect regular JavaScript timers. When you schedule an alarm, Chrome ensures it fires at approximately the scheduled time, making it ideal for time-critical operations.
Persistence is another key benefit. Alarms survive browser restarts and service worker terminations. When you create an alarm, Chrome stores it persistently and will fire it even if the browser was closed and reopened. This is essential for extensions that need to perform daily tasks, weekly reports, or any operation that should run on a schedule.
Efficiency comes from Chrome’s ability to optimize alarm firing. The browser can batch multiple alarms together and wake up the extension only when needed, rather than having each extension maintain its own timer. This approach improves overall system performance and battery life.
Common Use Cases
The Chrome Alarms API is perfect for numerous practical applications in extension development:
- Data Synchronization: Periodically fetch updated data from servers for news aggregators, stock price trackers, or task management tools
- Background Refresh: Update cached content, refresh authentication tokens, or check for new content
- Scheduled Notifications: Remind users about important events, deadlines, or tasks
- Maintenance Tasks: Clear old cache data, rotate logs, or clean up temporary storage
- Analytics Collection: Gather and send usage statistics at regular intervals
Setting Up Your Extension for Alarms
Before implementing the Alarms API, you need to properly configure your extension’s manifest file and understand the permission requirements.
Required Permissions
Open your extension’s manifest.json file and add the required permissions. The Alarms API requires the "alarms" permission to create, manage, and respond to scheduled alarms.
{
"manifest_version": 3,
"name": "Scheduled Task Extension",
"version": "1.0",
"description": "An extension that demonstrates the Chrome Alarms API",
"permissions": [
"alarms"
],
"background": {
"service_worker": "background.js"
}
}
The Alarms API works effectively with service workers in Manifest V3, which is the current standard for Chrome extensions. Unlike some other Chrome APIs, alarms will fire even when the service worker has been terminated, ensuring your scheduled tasks execute reliably.
Understanding Alarm Granularity
Chrome imposes limitations on alarm granularity to balance precision with battery efficiency. The minimum interval for repeating alarms is one minute, and Chrome may clamp very short delays to a minimum of approximately 45 seconds. This means the Alarms API is not suitable for sub-minute precision timing, but it is perfect for most scheduled task use cases.
Core Methods of the Chrome Alarms API
The Chrome Alarms API provides a comprehensive set of methods for creating, managing, and responding to alarms. Let’s explore each of these methods in detail.
Creating Alarms
The chrome.alarms.create() method schedules new alarms. You can create both one-time alarms and repeating alarms:
// Create a one-time alarm that fires after 30 minutes
chrome.alarms.create('one-time-alarm', {
delayInMinutes: 30
});
// Create a repeating alarm that fires every hour
chrome.alarms.create('repeating-alarm', {
delayInMinutes: 60,
periodInMinutes: 60
});
// Create an alarm that fires at a specific time
chrome.alarms.create('specific-time-alarm', {
when: Date.now() + (60 * 60 * 1000) // One hour from now
});
Querying Alarms
To get information about all active alarms, use the chrome.alarms.getAll() method:
chrome.alarms.getAll((alarms) => {
alarms.forEach((alarm) => {
console.log(`Alarm: ${alarm.name}`);
console.log(`Scheduled time: ${new Date(alarm.scheduledTime)}`);
console.log(`Period: ${alarm.periodInMinutes} minutes`);
});
});
Getting Specific Alarms
You can retrieve information about a specific alarm using its name:
chrome.alarms.get('my-alarm', (alarm) => {
if (alarm) {
console.log('Alarm exists:', alarm.name);
console.log('Next firing time:', new Date(alarm.scheduledTime));
} else {
console.log('Alarm not found');
}
});
Clearing Alarms
To remove alarms, use either chrome.alarms.clear() for a specific alarm or chrome.alarms.clearAll() to remove all alarms:
// Clear a specific alarm
chrome.alarms.clear('my-alarm');
// Clear all alarms
chrome.alarms.clearAll();
Listening for Alarm Events
To respond when an alarm fires, you need to set up an alarm listener in your service worker or background script:
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'repeating-alarm') {
console.log('Repeating alarm fired!');
// Perform your scheduled task here
performScheduledTask();
} else if (alarm.name === 'one-time-alarm') {
console.log('One-time alarm fired!');
handleOneTimeTask();
}
});
function performScheduledTask() {
// Your scheduled task logic here
console.log('Executing scheduled task at:', new Date().toISOString());
}
function handleOneTimeTask() {
// Handle one-time task
console.log('Handling one-time task');
}
Practical Examples
Let’s explore some practical examples that demonstrate how to implement common scheduling patterns in your Chrome extensions.
Example 1: Periodic Data Sync
Here’s how to implement a periodic data synchronization feature:
// background.js
// Create an alarm for periodic data sync
function scheduleDataSync(intervalMinutes = 15) {
chrome.alarms.create('data-sync', {
delayInMinutes: intervalMinutes,
periodInMinutes: intervalMinutes
});
}
// Initialize sync on extension install
chrome.runtime.onInstalled.addListener(() => {
scheduleDataSync(15);
console.log('Data sync scheduled');
});
// Handle the sync alarm
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'data-sync') {
syncData();
}
});
async function syncData() {
try {
// Fetch new data from your server
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Store the data using the Storage API
await chrome.storage.local.set({ cachedData: data, lastSync: Date.now() });
console.log('Data synced successfully');
// Notify the user if needed
notifyUserOfUpdate();
} catch (error) {
console.error('Data sync failed:', error);
}
}
function notifyUserOfUpdate() {
chrome.storage.local.get(['notificationsEnabled'], (result) => {
if (result.notificationsEnabled) {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/icon48.png',
title: 'Data Updated',
message: 'Your data has been synchronized'
});
}
});
}
Example 2: Daily Reminder System
Here’s how to implement a daily reminder system:
// background.js
// Schedule a daily reminder at a specific time
function scheduleDailyReminder(hour = 9, minute = 0) {
const now = new Date();
const targetTime = new Date();
targetTime.setHours(hour, minute, 0, 0);
// If the time has passed today, schedule for tomorrow
if (targetTime <= now) {
targetTime.setDate(targetTime.getDate() + 1);
}
const delayInMinutes = (targetTime - now) / (1000 * 60);
chrome.alarms.create('daily-reminder', {
delayInMinutes: delayInMinutes,
periodInMinutes: 24 * 60 // 24 hours in minutes
});
console.log(`Daily reminder scheduled for ${targetTime}`);
}
// Handle the daily reminder
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'daily-reminder') {
showReminderNotification();
}
});
function showReminderNotification() {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/icon48.png',
title: 'Daily Reminder',
message: "Don't forget to check your tasks for today!",
priority: 1
});
}
// Allow users to configure reminder time
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'setReminder') {
scheduleDailyReminder(message.hour, message.minute);
sendResponse({ success: true });
}
});
Example 3: Cache Expiration Management
Here’s how to implement automatic cache expiration:
// background.js
const CACHE_EXPIRY_ALARM = 'cache-expiry-check';
// Check cache expiry every hour
function scheduleCacheCheck() {
chrome.alarms.create(CACHE_EXPIRY_ALARM, {
delayInMinutes: 60,
periodInMinutes: 60
});
}
// Handle cache expiration check
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === CACHE_EXPIRY_ALARM) {
checkCacheExpiration();
}
});
async function checkCacheExpiration() {
const { cachedItems } = await chrome.storage.local.get('cachedItems');
if (!cachedItems) return;
const now = Date.now();
const expiredItems = [];
const validItems = [];
cachedItems.forEach(item => {
if (item.expiryTime && item.expiryTime < now) {
expiredItems.push(item.key);
} else {
validItems.push(item);
}
});
// Remove expired items
if (expiredItems.length > 0) {
await chrome.storage.local.remove(expiredItems);
console.log(`Removed ${expiredItems.length} expired cache items`);
}
// Update the valid items
await chrome.storage.local.set({ cachedItems: validItems });
}
// Function to store items with expiration
async function cacheWithExpiry(key, value, expiryMinutes) {
const expiryTime = Date.now() + (expiryMinutes * 60 * 1000);
const { cachedItems = [] } = await chrome.storage.local.get('cachedItems');
// Remove existing entry for this key if present
const filteredItems = cachedItems.filter(item => item.key !== key);
// Add new entry
filteredItems.push({ key, value, expiryTime });
await chrome.storage.local.set({ cachedItems: filteredItems });
// Ensure the cache check alarm is running
scheduleCacheCheck();
}
Best Practices and Common Pitfalls
When working with the Chrome Alarms API, following best practices will help you build more reliable and efficient extensions.
Best Practice 1: Handle Service Worker Lifecycle
Service workers in Manifest V3 can be terminated when inactive. Your alarm listener must be set up to handle this:
// Always set up listeners at the top level of your service worker
chrome.alarms.onAlarm.addListener((alarm) => {
handleAlarm(alarm);
});
// Use the 'when' property for more reliable scheduling
// than delayInMinutes when the exact time matters
function scheduleExactTime(alarmName, targetTime) {
chrome.alarms.create(alarmName, {
when: targetTime
});
}
Best Practice 2: Implement Idempotent Operations
Since alarms can fire more than once or be missed, make your operations idempotent:
// Bad: Creating duplicate records
async function saveNotification(message) {
await chrome.storage.local.set({
latestNotification: message,
notificationTime: Date.now()
});
// This could create duplicates!
}
// Good: Checking before creating
async function saveNotification(message) {
const { lastNotification, lastNotificationTime } =
await chrome.storage.local.get(['lastNotification', 'lastNotificationTime']);
// Only save if it's different or after a minimum interval
if (message !== lastNotification ||
Date.now() - lastNotificationTime > 60000) {
await chrome.storage.local.set({
latestNotification: message,
lastNotificationTime: Date.now()
});
}
}
Best Practice 3: Handle Edge Cases
Always handle edge cases in your alarm implementations:
chrome.alarms.onAlarm.addListener((alarm) => {
try {
handleAlarm(alarm);
} catch (error) {
console.error('Error handling alarm:', error);
// Attempt recovery or reschedule
if (alarm.name === 'critical-alarm') {
chrome.alarms.create(alarm.name, {
delayInMinutes: 5
});
}
}
});
Common Pitfalls to Avoid
-
Setting intervals too short: Remember that the minimum is one minute. Don’t try to use the Alarms API for sub-minute timing.
-
Not handling missing alarms: Always check if an alarm exists before trying to get or clear it.
-
Forgetting to reschedule: If an alarm is one-time and you need it to repeat, create a new repeating alarm when the one-time alarm fires.
-
Ignoring storage quota: When storing data triggered by alarms, remember that Chrome has storage limits.
Advanced Patterns
Multiple Alarms with Priority
For critical alarms, you can implement a priority system:
const PRIORITY_ALARMS = ['critical-sync', 'backup-data', 'urgent-notification'];
chrome.alarms.onAlarm.addListener((alarm) => {
const priority = PRIORITY_ALARMS.indexOf(alarm.name);
if (priority !== -1) {
console.log(`High priority alarm: ${alarm.name}`);
// Handle high priority alarm immediately
} else {
console.log(`Regular alarm: ${alarm.name}`);
// Handle regular alarm
}
});
Adaptive Scheduling
You can implement adaptive scheduling based on user activity:
async function adaptiveSchedule() {
const { syncInterval = 15 } = await chrome.storage.local.get('syncInterval');
// Check if user is active
const { idleInfo } = await chrome.idle.queryState(15);
if (idleInfo.state === 'active') {
// User is active, use normal interval
chrome.alarms.create('adaptive-sync', {
delayInMinutes: syncInterval,
periodInMinutes: syncInterval
});
} else {
// User is idle, use longer interval to save resources
chrome.alarms.create('adaptive-sync', {
delayInMinutes: syncInterval * 4,
periodInMinutes: syncInterval * 4
});
}
}
Conclusion
The Chrome Extension Alarms API is an essential tool for building powerful and reliable Chrome extensions that require time-based automation. By understanding its capabilities and limitations, you can create extensions that perform scheduled tasks efficiently without draining battery life or impacting browser performance.
Key takeaways from this guide include understanding the reliability and persistence advantages of the Alarms API over traditional JavaScript timers, properly configuring your manifest with the required permissions, implementing proper alarm lifecycle management, and following best practices for building robust scheduled operations.
Whether you’re building a data synchronization tool, a reminder application, or any extension that needs to perform periodic tasks, the Alarms API provides the foundation you need for reliable time-based functionality in your Chrome extensions.