EN/CH Mode
BRUCE_FE JS Interview Notes - Implementing Promise Timeout Control
Learn how to implement Promise timeout control mechanisms, master time management techniques for asynchronous tasks, and enhance frontend application stability and interview competitiveness.
EN/CH Mode
Lazy to read articles? Then watch videos!
Promise Timeout Control Overview
In practical development, we often need to set timeout limits for asynchronous operations to avoid long waits or infinite blocking. Promise timeout control is an important technique that ensures application responsiveness and stability.
Importance of Timeout Control
- 1.
Prevent Infinite Waiting
: Avoid Promises staying in pending state forever - 2.
Enhance User Experience
: Respond to user operations promptly, avoid interface freezing - 3.
Resource Management
: Prevent resource leaks and system overload - 4.
Error Handling
: Provide clear timeout error information
In frontend interviews, implementing Promise timeout control is an important topic to test candidates' understanding of asynchronous processing, error handling, and time management.
Basic Implementation: Promise Timeout Control
Below is a basic Promise timeout control implementation:
// Basic Promise timeout control function
function timeoutPromise(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Operation timeout (${timeout}ms)`));
}, timeout);
})
]);
}
// Usage example
const fetchWithTimeout = (url, timeout = 5000) => {
return timeoutPromise(
fetch(url),
timeout
);
};
// Test timeout control
fetchWithTimeout('https://api.example.com/data', 3000)
.then(response => response.json())
.then(data => {
console.log('Data fetched successfully:', data);
})
.catch(error => {
if (error.message.includes('timeout')) {
console.error('Request timeout, please try again later');
} else {
console.error('Request failed:', error);
}
});
Advanced Implementation: Cancellable Promise
Besides timeout control, we can also implement cancellable Promises to provide more flexible asynchronous operation control:
// Cancellable Promise implementation
function cancellablePromise(promise) {
let isCancelled = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise
.then(result => {
if (!isCancelled) {
resolve(result);
}
})
.catch(error => {
if (!isCancelled) {
reject(error);
}
});
});
// Return Promise and cancel function
return {
promise: wrappedPromise,
cancel: () => {
isCancelled = true;
}
};
}
// Combine timeout and cancel functionality
function timeoutCancellablePromise(promise, timeout) {
const { promise: wrappedPromise, cancel } = cancellablePromise(promise);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
cancel();
reject(new Error(`Operation timeout (${timeout}ms)`));
}, timeout);
});
return Promise.race([wrappedPromise, timeoutPromise]);
}
// Usage example
const longRunningTask = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('Task completed');
}, 10000); // 10 seconds
});
};
// Set 5 second timeout
timeoutCancellablePromise(longRunningTask(), 5000)
.then(result => {
console.log('Task successful:', result);
})
.catch(error => {
console.error('Task failed:', error.message);
});
Practical Application Scenarios
Promise timeout control has many application scenarios in practical development:
1. API Request Timeout Control
// API request timeout control
class ApiClient {
constructor(baseURL, defaultTimeout = 10000) {
this.baseURL = baseURL;
this.defaultTimeout = defaultTimeout;
}
async request(endpoint, options = {}) {
const { timeout = this.defaultTimeout, ...fetchOptions } = options;
const url = `${this.baseURL}${endpoint}`;
try {
const response = await timeoutPromise(
fetch(url, fetchOptions),
timeout
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
if (error.message.includes('timeout')) {
throw new Error('Request timeout, please check network connection');
}
throw error;
}
}
get(endpoint, timeout) {
return this.request(endpoint, { timeout });
}
post(endpoint, data, timeout) {
return this.request(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
timeout
});
}
}
// Usage example
const apiClient = new ApiClient('https://api.example.com', 5000);
apiClient.get('/users', 3000)
.then(users => {
console.log('User list:', users);
})
.catch(error => {
console.error('Failed to fetch users:', error.message);
});
2. User Operation Timeout Handling
// User operation timeout handling
class UserActionManager {
constructor() {
this.pendingActions = new Map();
}
// Execute user action with timeout
async executeAction(actionId, actionPromise, timeout = 30000) {
// If same action is already running, cancel it first
if (this.pendingActions.has(actionId)) {
this.cancelAction(actionId);
}
const { promise, cancel } = cancellablePromise(actionPromise);
// Store cancel function
this.pendingActions.set(actionId, cancel);
try {
const result = await timeoutCancellablePromise(promise, timeout);
this.pendingActions.delete(actionId);
return result;
} catch (error) {
this.pendingActions.delete(actionId);
if (error.message.includes('timeout')) {
// Show user-friendly timeout message
this.showTimeoutMessage(actionId);
}
throw error;
}
}
// Cancel specific action
cancelAction(actionId) {
const cancel = this.pendingActions.get(actionId);
if (cancel) {
cancel();
this.pendingActions.delete(actionId);
}
}
// Cancel all actions
cancelAllActions() {
this.pendingActions.forEach(cancel => cancel());
this.pendingActions.clear();
}
// Show timeout message
showTimeoutMessage(actionId) {
console.warn(`Operation ${actionId} timeout, please try again later`);
// Can show UI notification here
}
}
// Usage example
const actionManager = new UserActionManager();
// Simulate long operation
const longOperation = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('Operation completed');
}, 5000);
});
};
// Execute action with 3 second timeout
actionManager.executeAction('upload-file', longOperation(), 3000)
.then(result => {
console.log('Operation successful:', result);
})
.catch(error => {
console.error('Operation failed:', error.message);
});