文章中英模式
布魯斯前端JS面試題目 - 實作 Promise Sleep 函數
學習如何實作 JavaScript 中的 Promise Sleep 函數,處理延遲執行、定時任務與動畫,掌握非同步程式設計的基礎工具。
文章中英模式
懶得看文章?那就來看影片吧
Promise Sleep 函數概述
Promise Sleep 函數是一個簡單但實用的工具,它能讓程式碼暫停執行指定的時間。在各種非同步操作中,sleep 函數非常有用,尤其是需要延遲執行、模擬網路延遲或控制動畫節奏時。
為什麼需要 Sleep 函數?
- 1.
控制執行節奏
:在連續操作之間添加延遲,避免過度頻繁的操作 - 2.
模擬網路延遲
:測試程式碼對延遲的處理能力 - 3.
動畫與視覺效果
:控制連續視覺變化的間隔 - 4.
API 限流
:避免短時間內發送過多請求 - 5.
重試機制
:實現指數退避等重試策略
基本 Promise Sleep 實作
下面是一個簡單的 Promise Sleep 函數實作:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 使用範例
async function example() {
console.log('開始');
await sleep(2000); // 暫停 2 秒
console.log('2秒後'); // 2秒後執行
}
example();
這個函數創建了一個新的 Promise,在指定的毫秒數後調用 resolve。結合 async/await 語法,可以讓程式碼暫停執行指定的時間。
進階 Promise Sleep 功能
我們可以擴展基本的 sleep 函數,增加更多功能:
帶返回值的 Sleep
function sleepWithValue(ms, value) {
return new Promise(resolve => setTimeout(() => resolve(value), ms));
}
// 使用范例
async function example() {
console.log('开始');
const result = await sleepWithValue(2000, '延迟返回的数据');
console.log(result); // 2秒后显示:延迟返回的数据
}
example();
可取消的 Sleep
function cancellableSleep(ms) {
let timeoutId;
const promise = new Promise(resolve => {
timeoutId = setTimeout(resolve, ms);
});
// 添加取消方法
promise.cancel = function() {
clearTimeout(timeoutId);
};
return promise;
}
// 使用範例
async function example() {
console.log('開始');
const sleepPromise = cancellableSleep(5000);
// 2秒後取消休眠
setTimeout(() => {
console.log('取消休眠');
sleepPromise.cancel();
}, 2000);
try {
await sleepPromise;
console.log('5秒後'); // 不會執行,因為已取消
} catch (error) {
console.log('Sleep 被取消');
}
}
example();
實際應用場景
以下是一些 Promise Sleep 函數的實際應用:
1. 實現 API 請求重試機制
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
const { retryDelay = 1000 } = options;
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
// 嘗試發起請求
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.log(`嘗試 ${attempt + 1} 失敗: ${error.message}`);
lastError = error;
if (attempt < maxRetries - 1) {
// 使用指數退避策略計算下次重試延遲
const delay = retryDelay * Math.pow(2, attempt);
console.log(`等待 ${delay}ms 後重試...`);
await sleep(delay);
}
}
}
throw lastError;
}
// 使用範例
async function getData() {
try {
const data = await fetchWithRetry('https://api.example.com/data');
console.log('成功獲取數據:', data);
} catch (error) {
console.error('所有重試都失敗了:', error);
}
}
getData();
2. 創建動畫序列
async function animateSequence(element) {
// 設置初始狀態
element.style.opacity = '0';
element.style.transform = 'translateY(20px)';
element.style.transition = 'all 0.5s ease-out';
// 確保元素可見
element.style.display = 'block';
// 等待一幀以確保過渡效果生效
await sleep(50);
// 淡入動畫
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
// 等待淡入完成
await sleep(500);
// 添加強調效果
element.style.transform = 'scale(1.1)';
await sleep(200);
// 恢復正常大小
element.style.transform = 'scale(1)';
}
// 使用範例
document.getElementById('showButton').addEventListener('click', () => {
const notification = document.getElementById('notification');
animateSequence(notification);
});
3. 限流 API 請求
class RateLimiter {
constructor(requestsPerSecond) {
this.minDelayMs = 1000 / requestsPerSecond;
this.lastRequestTime = 0;
}
async limit() {
const now = Date.now();
const timeElapsed = now - this.lastRequestTime;
if (timeElapsed < this.minDelayMs) {
// 計算需要等待的時間
const waitTime = this.minDelayMs - timeElapsed;
await sleep(waitTime);
}
this.lastRequestTime = Date.now();
}
}
// 使用範例 - 每秒最多發送2個請求
const limiter = new RateLimiter(2);
async function fetchWithRateLimit(url) {
await limiter.limit();
return fetch(url).then(res => res.json());
}
// 示範多次調用
async function fetchMultipleData() {
console.log('開始獲取數據...');
// 這些請求將被限流,每秒最多執行2個
const results = await Promise.all([
fetchWithRateLimit('https://api.example.com/1'),
fetchWithRateLimit('https://api.example.com/2'),
fetchWithRateLimit('https://api.example.com/3'),
fetchWithRateLimit('https://api.example.com/4')
]);
console.log('所有數據獲取完成', results);
}
fetchMultipleData();