1. 项目背景与核心价值
在TypeScript开发中,我们经常遇到需要管理异步任务队列的场景。特别是在处理用户交互、网络请求或复杂计算时,一个健壮的异步队列管理系统能显著提升应用稳定性和用户体验。传统Promise队列存在几个痛点:
- 无法中途取消等待中的任务
- 相同请求可能重复创建Promise实例
- 未及时清理的引用导致内存泄漏
这个方案通过组合三个关键技术点解决上述问题:
- 可取消等待:允许开发者主动终止尚未执行的队列任务
- 单例Promise:确保相同请求共享Promise实例
- WeakRef清理:自动回收不再需要的任务引用
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 关键技术实现解析
2.1 可取消队列基础结构
typescript复制class CancelableQueue<T> {
private queue: Array<{
task: () => Promise<T>
cancelToken?: { canceled: boolean }
}> = []
private isProcessing = false
add(task: () => Promise<T>, cancelToken?: { canceled: boolean }) {
this.queue.push({ task, cancelToken })
if (!this.isProcessing) this.process()
}
}
关键设计点:
cancelToken作为取消信号载体- 队列状态与处理逻辑分离
- 非阻塞式任务添加
2.2 单例Promise管理
typescript复制const promiseCache = new Map<string, Promise<any>>()
function getSingletonPromise<T>(
key: string,
factory: () => Promise<T>
): Promise<T> {
if (!promiseCache.has(key)) {
const promise = factory().finally(() => {
promiseCache.delete(key)
})
promiseCache.set(key, promise)
}
return promiseCache.get(key)!
}
实现要点:
- 使用Map存储活跃Promise实例
- 自动清理已完成Promise
- 类型安全的泛型设计
2.3 WeakRef自动清理机制
typescript复制class WeakRefManager {
private refs = new Set<WeakRef<object>>()
private registry = new FinalizationRegistry((heldValue) => {
this.refs.delete(heldValue)
})
track(obj: object) {
const ref = new WeakRef(obj)
this.refs.add(ref)
this.registry.register(obj, ref)
return ref
}
}
内存管理策略:
- FinalizationRegistry监听对象GC
- 自动移除无效WeakRef
- 防止内存泄漏的兜底机制
3. 完整系统实现
3.1 组合式队列类
typescript复制class AdvancedAsyncQueue<T> {
private queue: Array<{
taskKey: string
task: () => Promise<T>
cancelToken?: { canceled: boolean }
}> = []
private weakRefManager = new WeakRefManager()
private singletonPromises = new Map<string, Promise<T>>()
async add(
taskKey: string,
task: () => Promise<T>,
cancelToken?: { canceled: boolean }
): Promise<T> {
// 实现细节...
}
private async process() {
// 队列处理逻辑...
}
}
3.2 核心处理流程
- 任务入队时生成唯一key
- 检查是否已存在相同key的Promise
- 创建可取消的包装任务
- 使用WeakRef跟踪任务相关对象
- 处理完成后自动清理资源
4. 实战应用场景
4.1 前端API请求管理
typescript复制const apiQueue = new AdvancedAsyncQueue<Response>()
// 组件内调用
const fetchUser = (userId: string) => {
const cancelToken = { canceled: false }
const promise = apiQueue.add(
`user_${userId}`,
() => fetch(`/api/users/${userId}`),
cancelToken
)
// 组件卸载时取消
onUnmounted(() => {
cancelToken.canceled = true
})
return promise
}
4.2 批量文件处理
typescript复制const fileQueue = new AdvancedAsyncQueue<string>()
async processFiles(files: File[]) {
const results = await Promise.all(
files.map(file =>
fileQueue.add(
file.name,
() => uploadFile(file)
)
)
)
// ...
}
5. 性能优化与调试
5.1 内存泄漏检测
typescript复制// 在开发环境添加调试钩子
if (process.env.NODE_ENV === 'development') {
setInterval(() => {
console.log(
'Active references:',
weakRefManager.getActiveCount()
)
}, 5000)
}
5.2 队列状态监控
typescript复制class AdvancedAsyncQueue {
// 添加监控方法
getStats() {
return {
queueLength: this.queue.length,
activePromises: this.singletonPromises.size,
memoryUsage: process.memoryUsage().heapUsed
}
}
}
6. 高级类型技巧
6.1 类型安全的CancelToken
typescript复制interface CancelToken {
readonly isCanceled: boolean
cancel(): void
}
function createCancelToken(): CancelToken {
let canceled = false
return {
get isCanceled() { return canceled },
cancel() { canceled = true }
}
}
6.2 链式调用支持
typescript复制class AdvancedAsyncQueue {
then<TResult>(
onfulfilled?: (value: T) => TResult | PromiseLike<TResult>
): Promise<TResult> {
return this.currentPromise.then(onfulfilled)
}
}
7. 测试策略
7.1 单元测试要点
typescript复制describe('AdvancedAsyncQueue', () => {
it('should cancel pending task', async () => {
const queue = new AdvancedAsyncQueue()
const cancelToken = { canceled: false }
const promise = queue.add('task1', longRunningTask, cancelToken)
cancelToken.canceled = true
await expect(promise).rejects.toThrow('Canceled')
})
})
7.2 内存泄漏测试
typescript复制it('should not hold references after completion', async () => {
const manager = new WeakRefManager()
let obj: object | null = { data: 'test' }
manager.track(obj)
obj = null
await new Promise(resolve => setTimeout(resolve, 0))
expect(manager.getActiveCount()).toBe(0)
})
8. 浏览器兼容方案
对于不支持WeakRef的环境,提供降级方案:
typescript复制class PolyfillWeakRefManager {
private refs = new Set<object>()
track(obj: object) {
this.refs.add(obj)
return {
deref: () => this.refs.has(obj) ? obj : undefined,
cleanup: () => this.refs.delete(obj)
}
}
}
9. 与现有生态集成
9.1 适配Redux中间件
typescript复制const queueMiddleware: Middleware = store => next => action => {
if (action.meta?.useQueue) {
return queue.add(action.type, () => Promise.resolve(next(action)))
}
return next(action)
}
9.2 Vue Composition API封装
typescript复制export function useAsyncQueue() {
const queue = new AdvancedAsyncQueue()
return {
addTask: queue.add.bind(queue),
cancelAll: queue.cancelAll.bind(queue)
}
}
10. 生产环境最佳实践
-
队列容量限制:防止内存溢出
typescript复制class BoundedAsyncQueue extends AdvancedAsyncQueue { constructor(private maxSize: number) { super() } add(taskKey: string, task: () => Promise<T>) { if (this.queue.length >= this.maxSize) { throw new Error('Queue overflow') } return super.add(taskKey, task) } } -
优先级队列扩展:
typescript复制interface PrioritizedTask { priority: number task: () => Promise<any> } class PriorityAsyncQueue extends AdvancedAsyncQueue { add(taskKey: string, task: () => Promise<any>, priority = 0) { // 按priority排序实现... } } -
超时处理增强:
typescript复制function withTimeout<T>( promise: Promise<T>, timeout: number ): Promise<T> { return Promise.race([ promise, new Promise<T>((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout) ) ]) }
