Appearance
并发请求处理
多个网络请求的并发策略:Promise.all、并发控制、请求合并。
1. 并发策略
1.1 Promise.all — 全部并发
typescript
// 并行请求(所有请求同时发起)
async function loadDashboard() {
try {
let [users, posts, comments, stats] = await Promise.all([
httpClient.request('/api/users', { method: http.HttpRequestMethod.GET }).then(r => r.result),
httpClient.request('/api/posts', { method: http.HttpRequestMethod.GET }).then(r => r.result),
httpClient.request('/api/comments', { method: http.HttpRequestMethod.GET }).then(r => r.result),
httpClient.request('/api/stats', { method: http.HttpRequestMethod.GET }).then(r => r.result)
])
return {
users: JSON.parse(users as string),
posts: JSON.parse(posts as string),
comments: JSON.parse(comments as string),
stats: JSON.parse(stats as string)
}
} catch (err) {
console.error('并发请求失败:', err)
}
}
// 总时间 = 最慢的那个请求的时间1.2 Promise.allSettled — 部分失败不中断
typescript
// 所有请求都执行完,无论成败
async function loadAllData() {
let results = await Promise.allSettled([
httpClient.request('/api/users', { method: http.HttpRequestMethod.GET }),
httpClient.request('/api/posts', { method: http.HttpRequestMethod.GET }),
httpClient.request('/api/comments', { method: http.HttpRequestMethod.GET })
])
for (let i = 0; i < results.length; i++) {
if (results[i].status === 'fulfilled') {
console.log(`请求 ${i} 成功`)
} else {
console.log(`请求 ${i} 失败:`, results[i].reason)
}
}
}1.3 串行请求(依赖关系)
typescript
// 串行请求(前一个完成后再发起下一个)
async function loadUserProfile(userId: string) {
// 先获取用户信息
let userResp = await httpClient.request(`/api/users/${userId}`, {
method: http.HttpRequestMethod.GET
})
let user = JSON.parse(userResp.result as string)
// 再获取用户动态(依赖用户 ID)
let postsResp = await httpClient.request(`/api/posts?userId=${userId}`, {
method: http.HttpRequestMethod.GET
})
return { user, posts: JSON.parse(postsResp.result as string) }
}2. 并发控制
2.1 并发限制(防止过多请求)
typescript
// 并发控制:限制同时进行的请求数
class ConcurrencyController {
private maxConcurrency: number = 5
private activeCount: number = 0
private queue: Array<() => Promise<any>> = []
async execute(task: () => Promise<any>): Promise<any> {
return new Promise((resolve, reject) => {
let taskWrapper = () => {
this.activeCount++
task()
.then(resolve)
.catch(reject)
.finally(() => {
this.activeCount--
this.nextTask()
})
}
if (this.activeCount < this.maxConcurrency) {
this.activeCount++
taskWrapper()
} else {
this.queue.push(taskWrapper)
}
})
}
private nextTask(): void {
if (this.queue.length > 0) {
let next = this.queue.shift()!
next()
}
}
}
// 使用
let controller = new ConcurrencyController()
// 最多 5 个并发请求
for (let i = 0; i < 20; i++) {
controller.execute(() => httpClient.request(`/api/data/${i}`))
}3. 请求合并
3.1 请求去重
typescript
// 请求去重:相同 URL 只请求一次
class RequestDeduplicator {
private pendingRequests: Map<string, Promise<any>> = new Map()
request(url: string, options: http.HttpRequestOptions): Promise<any> {
let key = `${options.method}:${url}`
if (this.pendingRequests.has(key)) {
// 已有相同请求,返回同一个 Promise
return this.pendingRequests.get(key)!
}
let promise = httpClient.request(url, options).then(r => r.result)
this.pendingRequests.set(key, promise)
promise.finally(() => {
this.pendingRequests.delete(key)
})
return promise
}
}3.2 批量请求
typescript
// 批量请求:将多个小请求合并为一个大请求
class BatchRequester {
private batchInterval: number = 100 // 批量间隔(毫秒)
private queue: Array<{ url: string, resolve: Function, reject: Function }> = []
batchRequest(url: string, options: http.HttpRequestOptions): Promise<any> {
return new Promise((resolve, reject) => {
this.queue.push({ url, resolve, reject })
// 等待批量间隔
if (this.queue.length === 1) {
setTimeout(() => {
this.flushBatch()
}, this.batchInterval)
}
})
}
private async flushBatch(): Promise<void> {
let urls = this.queue.map(item => item.url)
this.queue = []
// 合并请求
let allPromises = urls.map(url =>
httpClient.request(url, { method: http.HttpRequestMethod.GET })
)
let results = await Promise.all(allPromises)
for (let i = 0; i < this.queue.length; i++) {
this.queue[i].resolve(results[i].result)
}
}
}4. 请求取消
4.1 取消请求
typescript
// 取消请求
let httpClient = http.createHttp()
// 启动请求
let promise = httpClient.request('https://api.example.com/data', {
method: http.HttpRequestMethod.GET
})
// 取消请求
httpClient.cancelAll()
// 使用 AbortController(新版)
let controller = new AbortController()
let httpClient2 = http.createHttp()
httpClient2.request('https://api.example.com/data', {
method: http.HttpRequestMethod.GET,
signal: controller.signal
}).then((response) => {
console.log(response.result)
})
// 取消
controller.abort()5. 面试高频考点
Q1: Promise.all 和 Promise.allSettled 的区别?
回答:Promise.all 任一失败全部中断;Promise.allSettled 所有请求都执行完,无论成败,可以逐个处理结果。
Q2: 如何控制并发数?
回答:用信号量或队列控制并发数,防止过多请求导致服务器压力。限制同时进行的请求数,排队等待。
Q3: 请求去重/合并有什么用?
回答:去重防止重复请求浪费资源;合并减少请求次数,降低网络开销和服务器压力。
🐱 小猫提示:并发处理记住 "Promise.all 并行、并发控制防过载、请求去重防重复、cancelAll 取消"。