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!
This template demonstrates robust error handling strategies including retry mechanisms with exponential backoff, circuit breaker patterns, and graceful degradation for building resilient API-driven applications.
activity-09-error-handling/
├── index.html # Error handling dashboard and controls
├── styles.css # Comprehensive error UI styling
├── script.js # Complete error handling implementation
├── package.json # Project dependencies
└── README.md # This documentation
python3 -m http.server 8002
http://localhost:8002
class ErrorHandler {
async makeRequestWithRetry(fn, maxRetries = 3) {
let attempt = 0;
let lastError;
while (attempt <= maxRetries) {
try {
return await fn();
} catch (error) {
lastError = error;
attempt++;
if (attempt <= maxRetries && this.isRetryableError(error)) {
const delay = this.calculateBackoffDelay(attempt - 1);
await this.delay(delay);
} else {
throw lastError;
}
}
}
}
}
calculateBackoffDelay(attemptNumber) {
switch (this.backoffType) {
case 'exponential':
return this.baseDelay * Math.pow(2, attemptNumber);
case 'linear':
return this.baseDelay * (attemptNumber + 1);
case 'fixed':
return this.baseDelay;
default:
return this.baseDelay;
}
}
isRetryableError(error) {
const retryablePatterns = [
/timeout/i, /500/i, /502/i, /503/i, /504/i, /429/i
];
const nonRetryablePatterns = [
/400/i, /401/i, /403/i, /404/i, /422/i
];
// Check non-retryable first
if (nonRetryablePatterns.some(pattern => pattern.test(error.message))) {
return false;
}
return retryablePatterns.some(pattern => pattern.test(error.message));
}
class CircuitBreaker {
constructor(threshold = 3, timeout = 30000) {
this.threshold = threshold;
this.timeout = timeout;
this.failureCount = 0;
this.lastFailureTime = null;
this.isOpen = false;
}
async execute(fn) {
if (this.isOpen) {
const timeSinceLastFailure = Date.now() - this.lastFailureTime;
if (timeSinceLastFailure < this.timeout) {
throw new Error('Circuit breaker is open');
}
this.isOpen = false; // Half-open state
}
try {
const result = await fn();
this.reset(); // Success - reset failure count
return result;
} catch (error) {
this.recordFailure();
throw error;
}
}
recordFailure() {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.threshold) {
this.isOpen = true;
}
}
reset() {
this.failureCount = 0;
this.isOpen = false;
}
}
async function withTimeout(promise, timeoutMs) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Request timeout')), timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
calculateBackoffWithJitter(attempt, baseDelay) {
const exponentialDelay = baseDelay * Math.pow(2, attempt);
const jitter = Math.random() * 1000; // Add randomness
return exponentialDelay + jitter;
}
class AdaptiveRetryStrategy {
constructor() {
this.recentSuccessRate = 1.0;
this.adaptiveMultiplier = 1.0;
}
getRetryDelay(attempt, baseDelay) {
// Adjust delay based on recent success rate
const adaptiveDelay = baseDelay * this.adaptiveMultiplier;
return adaptiveDelay * Math.pow(2, attempt);
}
updateSuccessRate(isSuccess) {
// Update running average of success rate
this.recentSuccessRate = this.recentSuccessRate * 0.9 + (isSuccess ? 1 : 0) * 0.1;
this.adaptiveMultiplier = 2.0 - this.recentSuccessRate; // Increase delay when success rate is low
}
}
class BulkheadRequestManager {
constructor(pools = { critical: 5, normal: 10, background: 3 }) {
this.pools = {};
Object.keys(pools).forEach(pool => {
this.pools[pool] = {
active: 0,
max: pools[pool],
queue: []
};
});
}
async request(fn, priority = 'normal') {
const pool = this.pools[priority];
if (pool.active >= pool.max) {
// Queue the request
return new Promise((resolve, reject) => {
pool.queue.push({ fn, resolve, reject });
});
}
return this.executeRequest(fn, pool);
}
async executeRequest(fn, pool) {
pool.active++;
try {
return await fn();
} finally {
pool.active--;
this.processQueue(pool);
}
}
}
const metrics = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
totalRetries: 0,
averageResponseTime: 0,
successRate: 0,
errorsByType: {},
retryDistribution: {}
};
class ErrorMonitor {
constructor() {
this.metrics = new Map();
this.alertThresholds = {
errorRate: 0.1, // 10%
responseTime: 5000, // 5 seconds
retryRate: 0.3 // 30%
};
}
recordRequest(duration, success, errorType, retryCount) {
// Update metrics
this.updateMetrics(duration, success, errorType, retryCount);
// Check alerts
this.checkAlerts();
}
checkAlerts() {
const errorRate = this.calculateErrorRate();
const avgResponseTime = this.calculateAverageResponseTime();
if (errorRate > this.alertThresholds.errorRate) {
this.triggerAlert('High error rate detected', errorRate);
}
if (avgResponseTime > this.alertThresholds.responseTime) {
this.triggerAlert('High response time detected', avgResponseTime);
}
}
}
class RobustHTTPClient {
constructor(baseURL, defaultOptions = {}) {
this.baseURL = baseURL;
this.defaultOptions = {
timeout: 5000,
retries: 3,
...defaultOptions
};
}
async request(endpoint, options = {}) {
const config = { ...this.defaultOptions, ...options };
const url = `${this.baseURL}${endpoint}`;
return this.retryWithBackoff(async () => {
const response = await this.withTimeout(
fetch(url, config),
config.timeout
);
if (!response.ok) {
throw new HTTPError(response.status, response.statusText);
}
return response.json();
}, config.retries);
}
}
class APIErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// Log error to monitoring service
console.error('API Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<ErrorFallback
error={this.state.error}
onRetry={() => this.setState({ hasError: false, error: null })}
/>
);
}
return this.props.children;
}
}
Congratulations! You've completed all three advanced W3.5 activities. These templates provide a solid foundation for building resilient, performant, and user-friendly API-driven applications.