Student starter code (30% baseline)
index.html- Main HTML pagescript.js- JavaScript logicstyles.css- Styling and layoutpackage.json- Dependenciessetup.sh- Setup scriptREADME.md- Instructions (below)💡 Download the ZIP, extract it, and follow the instructions below to get started!
By completing this discovery challenge, you will:
python3 -m http.server 8002http://localhost:8002This is a complete reference implementation showcasing professional caching patterns:
You have a 100% working reference implementation to study and learn from!
Files to Study: script.js lines 1-52 (Cache Core Methods)
Key Learning Points:
setCache() stores data with expiry timestampsgetCache() checks expiration before returning dataExploration Tasks:
setCache() method - understand the cache data structuregetCache() - see how expiration is validatedhandleStorageQuotaExceeded() - learn cleanup strategiesFiles to Study: script.js lines 119-156 (loadDataWithCache method)
Key Learning Points:
Exploration Tasks:
loadDataWithCache() - understand the decision flowupdateCacheIndicator() - see how UI reflects cache statusFiles to Study: script.js lines 478-520 (CacheUtils object)
Key Learning Points:
Exploration Tasks:
staleWhileRevalidate() - understand async revalidationFiles to Study: script.js lines 33-52, 413-419
Key Learning Points:
Date.now() + ttlExploration Tasks:
setCache()applyCacheSettings() - see dynamic TTL updatesFiles to Study: script.js lines 421-443
Key Learning Points:
Exploration Tasks:
handleStorageQuotaExceeded() - understand the cleanup approachfindOldestCacheKey() - learn LRU implementationcalculateStorageUsage() - see size calculationReady to level up? Try these advanced implementations:
Goal: Implement cache versioning to handle API changes without manual cache clearing.
Requirements:
version field to cache data structureSuccess Criteria:
Goal: Implement memory cache (fast) + localStorage (persistent) combination.
Requirements:
Success Criteria:
Goal: Compress cached data to maximize storage capacity.
Requirements:
Success Criteria:
Goal: Sync cache updates across multiple browser tabs.
Requirements:
storage event listener to detect changesSuccess Criteria:
Goal: Preload likely-needed data to improve perceived performance.
Requirements:
Promise.all() for parallel preloadingSuccess Criteria:
// This is what each cache entry looks like in localStorage
{
data: { /* Your actual API response */ },
timestamp: 1704123456789, // When cached
expiry: 1704123756789, // When it expires
ttl: 300000 // TTL in milliseconds (5 min)
}
// 1. Cache-First (Best for stable data)
// Check cache → return if found → fetch if missing
const cacheFirst = async (key, fetchFn, ttl) => {
const cached = getCache(key);
if (cached) return cached.data;
const data = await fetchFn();
setCache(key, data, ttl);
return data;
};
// 2. Network-First (Best for dynamic data)
// Try network → fallback to cache on failure
const networkFirst = async (key, fetchFn, ttl) => {
try {
const data = await fetchFn();
setCache(key, data, ttl);
return data;
} catch (error) {
const cached = getCache(key);
if (cached) return cached.data;
throw error;
}
};
// 3. Stale-While-Revalidate (Best for user experience)
// Return cache immediately → refresh in background
const staleWhileRevalidate = async (key, fetchFn, ttl) => {
const cached = getCache(key);
if (cached) {
// Return cache immediately
fetchFn().then(data => setCache(key, data, ttl));
return cached.data;
}
// No cache - fetch fresh
const data = await fetchFn();
setCache(key, data, ttl);
return data;
};
// Track cache efficiency
const stats = { hits: 0, misses: 0 };
const hitRate = (stats.hits / (stats.hits + stats.misses)) * 100;
console.log(`Cache is ${hitRate.toFixed(1)}% effective`);
This implementation showcases professional patterns you should follow:
cache_users, cache_products, cache_analyticscache_Object.keys(localStorage).filter(k => k.startsWith('cache_'))cache_users_v2) for API changes// Replace mock functions with real endpoints
class RealAPIService {
async fetchUsers() {
const response = await fetch('https://api.example.com/users');
if (!response.ok) throw new Error('API request failed');
return response.json();
}
async fetchWithCache(endpoint, cacheKey, ttl = 300000) {
return CacheUtils.cacheFirst(cacheKey,
() => this.fetchAPI(endpoint),
ttl
);
}
}
// Custom hook for cached data in React
function useCachedData(key, fetchFunction, ttl = 300000) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
CacheUtils.cacheFirst(key, fetchFunction, ttl)
.then(setData)
.finally(() => setLoading(false));
}, [key]);
return { data, loading };
}
// Usage
const { data, loading } = useCachedData('users', fetchUsers, 5 * 60 * 1000);
// Composable for cached data in Vue 3
function useCachedData(key, fetchFunction, ttl = 300000) {
const data = ref(null);
const loading = ref(true);
onMounted(async () => {
data.value = await CacheUtils.cacheFirst(key, fetchFunction, ttl);
loading.value = false;
});
return { data, loading };
}
Problem: "Storage quota exceeded" error Solution: The app automatically removes oldest cache entries. To manually fix:
// Clear all cache
localStorage.clear();
// Or remove only cache entries
Object.keys(localStorage)
.filter(k => k.startsWith('cache_'))
.forEach(k => localStorage.removeItem(k));
Problem: Cache not updating after API changes Solution: Check TTL settings and manually clear cache, or implement versioning
// Add version to cache key
const cacheKey = `users_v2`; // Increment when API changes
Problem: Data seems stale even after expiration
Solution: Verify browser time is correct (TTL uses Date.now())
console.log('Current time:', new Date().toISOString());
console.log('Cache expiry:', new Date(cacheData.expiry).toISOString());
Inspect localStorage in DevTools:
cache_* entriesMonitor Cache Performance:
// Add to console to see hit/miss ratio
console.log(`Hits: ${cacheManager.stats.hits}, Misses: ${cacheManager.stats.misses}`);
console.log(`Hit Rate: ${((cacheManager.stats.hits / (cacheManager.stats.hits + cacheManager.stats.misses)) * 100).toFixed(1)}%`);
Test Cache Expiration:
// Set short TTL to test expiration behavior
cacheManager.cacheTTL = 10 * 1000; // 10 seconds
// Load data, wait 10 seconds, load again → should fetch fresh
Next Steps: Continue with Activity 9 (Error Handling) to learn robust error management that complements caching strategies.