文章中英模式
布魯斯前端JS面試題目 - 實作 Promise 超時控制
學習如何實作 Promise 超時控制機制,掌握非同步任務的時間管理技巧,提升前端應用穩定性與面試競爭力。
文章中英模式
懶得看文章?那就來看影片吧
Promise 超時控制概述
在實際開發中,我們經常需要為非同步操作設定超時限制,避免長時間等待或無限阻塞。Promise 超時控制是一種重要的技術,可以確保應用程式的響應性和穩定性。
超時控制的重要性
- 1.
防止無限等待
:避免 Promise 永遠處於 pending 狀態 - 2.
提升用戶體驗
:及時響應用戶操作,避免界面凍結 - 3.
資源管理
:防止資源洩漏和系統過載 - 4.
錯誤處理
:提供明確的超時錯誤信息
在前端面試中,實作 Promise 超時控制是測試候選人對非同步處理、錯誤處理和時間管理理解的重要題目。
基本實作:Promise 超時控制
以下是一個基本的 Promise 超時控制實作:
// 基本 Promise 超時控制函數
function timeoutPromise(promise, timeout) {
return Promise.race([
promise,
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`操作超時 (${timeout}ms)`));
}, timeout);
})
]);
}
// 使用示例
const fetchWithTimeout = (url, timeout = 5000) => {
return timeoutPromise(
fetch(url),
timeout
);
};
// 測試超時控制
fetchWithTimeout('https://api.example.com/data', 3000)
.then(response => response.json())
.then(data => {
console.log('數據獲取成功:', data);
})
.catch(error => {
if (error.message.includes('超時')) {
console.error('請求超時,請稍後重試');
} else {
console.error('請求失敗:', error);
}
});
進階實作:可取消的 Promise
除了超時控制,我們還可以實作可取消的 Promise,提供更靈活的非同步操作控制:
// 可取消的 Promise 實作
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);
}
});
});
// 返回 Promise 和取消函數
return {
promise: wrappedPromise,
cancel: () => {
isCancelled = true;
}
};
}
// 結合超時和取消功能
function timeoutCancellablePromise(promise, timeout) {
const { promise: wrappedPromise, cancel } = cancellablePromise(promise);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
cancel();
reject(new Error(`操作超時 (${timeout}ms)`));
}, timeout);
});
return Promise.race([wrappedPromise, timeoutPromise]);
}
// 使用示例
const longRunningTask = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('任務完成');
}, 10000); // 10秒
});
};
// 設定 5 秒超時
timeoutCancellablePromise(longRunningTask(), 5000)
.then(result => {
console.log('任務成功:', result);
})
.catch(error => {
console.error('任務失敗:', error.message);
});
實際應用場景
Promise 超時控制在實際開發中有很多應用場景:
1. API 請求超時控制
// API 請求超時控制
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('超時')) {
throw new Error('請求超時,請檢查網絡連接');
}
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
});
}
}
// 使用示例
const apiClient = new ApiClient('https://api.example.com', 5000);
apiClient.get('/users', 3000)
.then(users => {
console.log('用戶列表:', users);
})
.catch(error => {
console.error('獲取用戶失敗:', error.message);
});
2. 用戶操作超時處理
// 用戶操作超時處理
class UserActionManager {
constructor() {
this.pendingActions = new Map();
}
// 執行用戶操作,設定超時
async executeAction(actionId, actionPromise, timeout = 30000) {
// 如果已有相同操作在執行,先取消
if (this.pendingActions.has(actionId)) {
this.cancelAction(actionId);
}
const { promise, cancel } = cancellablePromise(actionPromise);
// 儲存取消函數
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('超時')) {
// 顯示用戶友好的超時提示
this.showTimeoutMessage(actionId);
}
throw error;
}
}
// 取消特定操作
cancelAction(actionId) {
const cancel = this.pendingActions.get(actionId);
if (cancel) {
cancel();
this.pendingActions.delete(actionId);
}
}
// 取消所有操作
cancelAllActions() {
this.pendingActions.forEach(cancel => cancel());
this.pendingActions.clear();
}
// 顯示超時提示
showTimeoutMessage(actionId) {
console.warn(`操作 ${actionId} 超時,請稍後重試`);
// 這裡可以顯示 UI 提示
}
}
// 使用示例
const actionManager = new UserActionManager();
// 模擬長時間操作
const longOperation = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('操作完成');
}, 5000);
});
};
// 執行操作,設定 3 秒超時
actionManager.executeAction('upload-file', longOperation(), 3000)
.then(result => {
console.log('操作成功:', result);
})
.catch(error => {
console.error('操作失敗:', error.message);
});