异步请求调度器是现代前端开发中处理并发请求、控制请求顺序和优先级的重要工具。下面我将详细介绍如何基于Promise和async/await设计一个高效的异步请求调度器。
一、核心概念与设计原理
-
Promise基础:Promise是异步编程的基础,它表示一个可能现在、将来或永远不可用的值。Promise有三种状态:Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。
-
async/await优势:async/await是建立在Promise之上的语法糖,使异步代码看起来像同步代码,提高可读性和可维护性。async函数隐式返回Promise,await用于等待Promise完成。
-
调度器目标:
- 控制并发请求数量
- 管理请求优先级
- 处理请求失败重试
- 提供请求取消功能
二、基础调度器实现
下面是一个基本的并发控制调度器实现:
class RequestScheduler {
constructor(maxConcurrent = 5) {
this.maxConcurrent = maxConcurrent;
this.queue = [];
this.activeCount = 0;
}
add(requestFn, priority = 0) {
return new Promise((resolve, reject) => {
this.queue.push({
requestFn,
resolve,
reject,
priority
});
this.queue.sort((a, b) => b.priority - a.priority);
this.run();
});
}
async run() {
if (this.activeCount >= this.maxConcurrent || !this.queue.length) return;
this.activeCount++;
const { requestFn, resolve, reject } = this.queue.shift();
try {
const result = await requestFn();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.activeCount--;
this.run();
}
}
}
三、高级功能扩展
1. 请求重试机制
class RetryScheduler extends RequestScheduler {
constructor(maxConcurrent = 5, maxRetries = 3) {
super(maxConcurrent);
this.maxRetries = maxRetries;
}
async run() {
if (this.activeCount >= this.maxConcurrent || !this.queue.length) return;
this.activeCount++;
const { requestFn, resolve, reject, retryCount = 0 } = this.queue.shift();
try {
const result = await requestFn();
resolve(result);
} catch (error) {
if (retryCount < this.maxRetries) {
this.queue.push({
requestFn,
resolve,
reject,
retryCount: retryCount + 1
});
this.run();
} else {
reject(error);
}
} finally {
this.activeCount--;
this.run();
}
}
}
2. 请求取消功能
class CancelableScheduler extends RequestScheduler {
add(requestFn, priority = 0) {
const controller = new AbortController();
const wrappedRequest = async () => {
if (controller.signal.aborted) {
throw new Error('Request aborted');
}
return requestFn(controller.signal);
};
const promise = super.add(wrappedRequest, priority);
return {
promise,
abort: () => controller.abort()
};
}
}
四、实际应用场景
1. 图片懒加载批量请求
const scheduler = new RequestScheduler(3); // 最多同时加载3张图片
async function lazyLoadImages(imageUrls) {
const loadImage = (url) =>
new Promise((resolve) => {
const img = new Image();
img.src = url;
img.onload = () => resolve(img);
});
const promises = imageUrls.map(url =>
scheduler.add(() => loadImage(url))
);
return Promise.all(promises);
}
2. API接口优先级调度
const apiScheduler = new RequestScheduler(2);
// 高优先级请求(用户操作触发)
async function submitFormData(data) {
return apiScheduler.add(
() => fetch('/api/submit', { method: 'POST', body: JSON.stringify(data) }),
10 // 高优先级
);
}
// 低优先级请求(后台预加载)
async function prefetchData() {
return apiScheduler.add(
() => fetch('/api/prefetch'),
1 // 低优先级
);
}
3. 文件分片上传
const uploadScheduler = new RetryScheduler(3, 2); // 并发3个,最多重试2次
async function uploadFile(file) {
const chunkSize = 1024 * 1024; // 1MB
const chunks = Math.ceil(file.size / chunkSize);
const uploadPromises = [];
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
uploadPromises.push(
uploadScheduler.add(() => uploadChunk(chunk, i, file.name))
);
}
await Promise.all(uploadPromises);
return completeUpload(file.name);
}
async function uploadChunk(chunk, index, filename) {
const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', index);
formData.append('filename', filename);
const response = await fetch('/api/upload-chunk', {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error('Upload failed');
return response.json();
}
五、性能优化建议
- 动态调整并发数:根据网络状况动态调整maxConcurrent值
- 请求去重:对相同URL或参数的请求进行缓存或合并
- 超时处理:为每个请求添加超时控制
- 错误隔离:防止单个请求失败影响整个调度器
- 内存管理:及时清理已完成请求的引用
六、总结
基于Promise和async/await的异步请求调度器通过以下方式提升应用性能:
- 有效控制并发请求数量,避免浏览器或服务器过载
- 通过优先级调度确保关键请求优先处理
- 简洁的代码结构,易于维护和扩展
- 统一的错误处理机制,提高应用稳定性