frontend
Promise with Time Limit in JavaScript
January 24, 2026
Promise with Time Limit in JavaScript
Overview
Promise with Time Limit is a technique to add timeout functionality to promises, ensuring they complete within a specified duration. If a promise takes longer than the time limit, it will be rejected with a timeout error.
Basic Implementation
/**
* Creates a function that wraps another function with a time limit
* @param {Function} fn - The function to wrap
* @param {number} t - Time limit in milliseconds
* @return {Function} - Wrapped function that enforces time limit
*/
var timeLimit = function (fn, t) {
return async function (...args) {
const promise = fn(...args);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject("Time Limit Exceeded");
}, t);
});
return Promise.race([promise, timeoutPromise]);
};
};
Example Usage
// Example 1: Async function with timeout
const limited = timeLimit((t) => new Promise((res) => setTimeout(res, t)), 100);
limited(150).catch(console.log); // "Time Limit Exceeded" at t=100ms
// Example 2: Synchronous function
const func = function (t) {
console.log(t);
};
const limited2 = timeLimit(func, 200);
limited2(150); // 150 (synchronous functions complete immediately)
Advanced Implementation
function timeLimit(fn, timeout, errorMessage = 'Time Limit Exceeded') {
return async function (...args) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(errorMessage));
}, timeout);
});
const executionPromise = Promise.resolve(fn(...args));
return Promise.race([executionPromise, timeoutPromise]);
};
}
Use Cases
1. API Calls with Timeout
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
const fetchWithTimeout = timeLimit(fetchUserData, 5000, 'API request timeout');
try {
const user = await fetchWithTimeout(123);
console.log(user);
} catch (error) {
if (error.message === 'API request timeout') {
console.error('Request took too long');
// Show fallback UI
} else {
console.error('Other error:', error);
}
}
2. Database Queries
async function queryDatabase(query) {
return db.query(query);
}
const queryWithTimeout = timeLimit(queryDatabase, 3000);
try {
const results = await queryWithTimeout('SELECT * FROM users');
} catch (error) {
console.error('Query timeout or error:', error);
}
3. Image Loading
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
const loadImageWithTimeout = timeLimit(loadImage, 5000, 'Image load timeout');
try {
const img = await loadImageWithTimeout('https://example.com/image.jpg');
document.body.appendChild(img);
} catch (error) {
console.error('Failed to load image:', error);
// Show placeholder image
}
Class-Based Implementation
class TimeLimitedPromise {
constructor(promise, timeout, errorMessage = 'Time Limit Exceeded') {
this.promise = promise;
this.timeout = timeout;
this.errorMessage = errorMessage;
this.timeoutId = null;
}
async execute() {
const timeoutPromise = new Promise((_, reject) => {
this.timeoutId = setTimeout(() => {
reject(new Error(this.errorMessage));
}, this.timeout);
});
try {
const result = await Promise.race([this.promise, timeoutPromise]);
clearTimeout(this.timeoutId);
return result;
} catch (error) {
clearTimeout(this.timeoutId);
throw error;
}
}
cancel() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
}
}
// Usage
const promise = fetch('/api/data');
const limited = new TimeLimitedPromise(promise, 5000);
try {
const data = await limited.execute();
} catch (error) {
console.error(error);
}
With AbortController
function timeLimitWithAbort(fn, timeout) {
return async function (...args) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const result = await fn(...args, { signal: controller.signal });
clearTimeout(timeoutId);
return result;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Time Limit Exceeded');
}
throw error;
}
};
}
Retry with Time Limit
async function retryWithTimeLimit(fn, timeout, maxRetries = 3) {
const limitedFn = timeLimit(fn, timeout);
for (let i = 0; i < maxRetries; i++) {
try {
return await limitedFn();
} catch (error) {
if (i === maxRetries - 1) throw error;
if (error.message === 'Time Limit Exceeded') {
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
} else {
throw error; // Don't retry on other errors
}
}
}
}
Batch Operations with Time Limit
async function batchWithTimeLimit(items, processor, itemTimeout, batchTimeout) {
const results = [];
const startTime = Date.now();
for (const item of items) {
if (Date.now() - startTime > batchTimeout) {
throw new Error('Batch timeout exceeded');
}
const limitedProcessor = timeLimit(processor, itemTimeout);
try {
const result = await limitedProcessor(item);
results.push({ success: true, result });
} catch (error) {
results.push({ success: false, error: error.message });
}
}
return results;
}
Error Handling Best Practices
async function safeTimeLimit(fn, timeout, fallback = null) {
try {
const limitedFn = timeLimit(fn, timeout);
return await limitedFn();
} catch (error) {
if (error.message === 'Time Limit Exceeded') {
console.warn('Operation timed out, using fallback');
return fallback !== null ? fallback : null;
}
throw error;
}
}
// Usage
const data = await safeTimeLimit(
fetchData,
5000,
{ default: 'data' } // Fallback value
);
Monitoring and Logging
function timeLimitWithMetrics(fn, timeout, metrics = {}) {
return async function (...args) {
const startTime = Date.now();
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
metrics.timeouts = (metrics.timeouts || 0) + 1;
reject(new Error('Time Limit Exceeded'));
}, timeout);
});
try {
const result = await Promise.race([fn(...args), timeoutPromise]);
const duration = Date.now() - startTime;
metrics.successes = (metrics.successes || 0) + 1;
metrics.avgDuration = ((metrics.avgDuration || 0) + duration) / metrics.successes;
return result;
} catch (error) {
const duration = Date.now() - startTime;
metrics.failures = (metrics.failures || 0) + 1;
throw error;
}
};
}
Best Practices
- Set Reasonable Timeouts: Balance between too short (false failures) and too long (poor UX)
- Handle Timeout Errors: Distinguish timeout errors from other errors
- Provide Fallbacks: Show alternative content when timeouts occur
- Log Timeouts: Track timeout occurrences for monitoring
- Clean Up Timers: Always clear timeouts to prevent memory leaks
- Consider Retry Logic: Retry transient failures with exponential backoff
- Use for I/O Operations: Best for network/database calls, not CPU-bound tasks
Common Patterns
Pattern 1: Fetch with Timeout
const fetchWithTimeout = timeLimit(fetch, 5000);
const response = await fetchWithTimeout('/api/data');
Pattern 2: Promise Chain with Timeout
const processWithTimeout = timeLimit(
async (data) => {
const step1 = await processStep1(data);
const step2 = await processStep2(step1);
return processStep3(step2);
},
10000
);
Pattern 3: Multiple Timeouts
const quickOperation = timeLimit(operation, 1000);
const slowOperation = timeLimit(operation, 10000);
try {
return await quickOperation();
} catch {
return await slowOperation(); // Fallback to longer timeout
}