异步任务互斥锁工具 (Async Lock Manager)
📖 概述
LockManager 是一个生产级的异步任务互斥锁管理工具,专为现代 Web 应用中的并发控制设计。它通过互斥锁机制防止异步任务重复执行,提供队列管理、智能重试、超时控制和资源自动清理等功能。
🎯 设计哲学
为什么需要异步任务互斥锁?
传统的防抖节流方案存在以下局限性:
-
无法防止长时间异步操作:防抖节流只能控制函数调用频率,但无法防止 API 接口长时间未返回时的重复调用
-
缺乏队列管理:多个并发请求无法有序排队执行
-
缺少取消机制:无法中断已发起的异步任务
-
资源管理困难:无法自动清理过期锁和等待任务
-
缺乏智能重试:简单的重试策略无法适应复杂错误场景
本工具的设计目标是解决这些问题,提供:
- ✅ 原子性操作:确保锁的获取和释放是原子操作
- ✅ 智能队列管理:支持 FIFO 队列,可配置队列大小和超时
- ✅ 可中断执行:支持任务取消和超时中断
- ✅ 指数退避重试:支持自定义重试条件和退避策略
- ✅ 资源自动管理:自动清理过期锁和队列项
- ✅ 完整监控统计:提供执行统计和状态监控
🆚 与传统方案对比
| 特性 |
防抖 (Debounce) |
节流 (Throttle) |
简单互斥锁 |
LockManager |
| 防止重复调用 |
✅ 时间窗口内 |
✅ 固定频率 |
✅ 直到完成 |
✅ 直到完成 + 队列 |
| 异步任务支持 |
❌ 有限 |
❌ 有限 |
✅ 基础 |
✅ 完整(重试、超时、取消) |
| 队列管理 |
❌ 不支持 |
❌ 不支持 |
❌ 不支持 |
✅ 支持 FIFO 队列 |
| 取消机制 |
❌ 不支持 |
❌ 不支持 |
❌ 不支持 |
✅ 支持主动取消 |
| 重试策略 |
❌ 不支持 |
❌ 不支持 |
❌ 不支持 |
✅ 指数退避 + 自定义条件 |
| 超时控制 |
❌ 不支持 |
❌ 不支持 |
❌ 不支持 |
✅ 支持任务和队列超时 |
| 资源清理 |
❌ 无 |
❌ 无 |
❌ 无 |
✅ 自动清理过期锁 |
| 状态监控 |
❌ 无 |
❌ 无 |
❌ 无 |
✅ 完整统计信息 |
🏗️ 架构设计
核心执行流程
// 执行流程:检查锁 → 加入队列(可选) → 获取锁 → 执行任务 → 释放锁 → 处理队列
async execute(options) {
// 1. 检查锁状态
// 2. 如果已锁定且启用队列,加入队列等待
// 3. 获取锁(原子操作)
// 4. 执行任务(支持重试)
// 5. 清理锁资源
// 6. 处理队列中的下一个任务
}
锁管理机制 (_acquireLock)
/**
* 原子性地获取锁
* 设计特点:
* 1. 三重检查:确保锁获取的原子性
* 2. 唯一标识:为每个锁尝试生成唯一ID
* 3. 资源预分配:提前创建取消控制器
* 4. 验证机制:设置后验证确保原子性
*/
_acquireLock(name) {
const attemptId = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
// 第一重检查
const existing = this._lockMap.get(name);
if (existing?.locked) {
return null;
}
// 创建锁对象
const lockItem = {
locked: true,
abortController: new AbortController(),
timeoutTimer: null,
createdAt: Date.now(),
taskId: `${name}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
attemptId: attemptId
};
// 第二重检查(原子性保障)
const current = this._lockMap.get(name);
if (current?.locked) {
return null;
}
// 设置锁
this._lockMap.set(name, lockItem);
// 最终验证(确保原子性)
const afterSet = this._lockMap.get(name);
if (afterSet?.attemptId !== attemptId) {
lockItem.abortController.abort();
return null;
}
return lockItem;
}
队列管理机制 (_addToQueue, _processNextInQueue)
/**
* 将任务加入等待队列
* 设计特点:
* 1. 容量控制:可配置最大队列大小
* 2. 超时管理:队列等待也有超时控制
* 3. 有序执行:FIFO(先进先出)原则
* 4. 资源清理:超时自动清理队列项
*/
_addToQueue(options) {
const { name, maxQueueSize } = options;
let queue = this._queueMap.get(name);
if (!queue) {
queue = [];
this._queueMap.set(name, queue);
}
// 队列容量检查
if (queue.length >= maxQueueSize) {
const error = new Error(`任务队列【${name}】已满(最大${maxQueueSize})`);
error.type = 'queue_full';
error.code = 'QUEUE_FULL';
return Promise.reject(error);
}
return new Promise((resolve, reject) => {
const queueItem = {
options,
resolve,
reject,
enqueuedAt: Date.now()
};
queue.push(queueItem);
// 队列等待超时
if (options.timeout > 0) {
queueItem.timeoutTimer = setTimeout(() => {
const index = queue.indexOf(queueItem);
if (index > -1) {
queue.splice(index, 1);
const error = new Error(`任务【${name}】在队列中等待超时`);
error.type = 'queue_timeout';
error.code = 'QUEUE_TIMEOUT';
reject(error);
}
}, options.timeout);
}
});
}
/**
* 处理队列中的下一个任务
* 设计特点:
* 1. 微任务调度:使用 Promise.resolve() 避免 setTimeout 延迟
* 2. 递归处理:自动处理队列中的所有任务
* 3. 资源清理:处理完成后清理空队列
* 4. 错误传播:正确处理任务成功和失败
*/
async _processNextInQueue(name) {
const queue = this._queueMap.get(name);
if (!queue || queue.length === 0) {
this._queueMap.delete(name);
return;
}
// 使用微任务处理,避免 setTimeout 的延迟
await Promise.resolve();
const queueItem = queue.shift();
// 清理队列项的超时定时器
if (queueItem.timeoutTimer) {
clearTimeout(queueItem.timeoutTimer);
}
try {
const result = await this._executeTask(queueItem.options);
queueItem.resolve(result);
} catch (error) {
queueItem.reject(error);
} finally {
// 递归处理下一个任务
if (queue.length > 0) {
Promise.resolve().then(() => this._processNextInQueue(name));
} else {
this._queueMap.delete(name);
}
}
}
智能重试机制 (_executeWithExponentialBackoff)
/**
* 指数退避重试执行
* 设计特点:
* 1. 取消检查:每次重试前检查取消信号
* 2. 退避算法:指数退避 + 随机抖动
* 3. 自定义条件:支持根据错误类型决定是否重试
* 4. 安全延迟:可中断的延时函数
*/
async _executeWithExponentialBackoff(fn, maxRetries, baseDelay, maxDelay, abortController, retryCondition) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
// 检查是否已取消
if (abortController.signal.aborted) {
const cancelError = new Error('任务已被取消');
cancelError.type = 'cancel';
cancelError.code = 'CANCELLED';
throw cancelError;
}
// 非首次尝试时延迟
if (attempt > 0) {
const delay = this._calculateExponentialBackoffDelay(attempt, baseDelay, maxDelay);
await this._sleep(delay, abortController.signal);
}
return await fn();
} catch (error) {
lastError = error;
// 判断是否应该重试
if (!this._shouldRetry(error, retryCondition)) {
throw error;
}
// 重试用尽
if (attempt === maxRetries) {
error.retryAttempts = attempt;
throw error;
}
}
}
throw lastError;
}
/**
* 判断是否应该重试(支持自定义重试条件)
*/
_shouldRetry(error, retryCondition) {
// 不重试的错误类型
const noRetryTypes = ['cancel', 'timeout', 'queue_full', 'queue_timeout', 'lock_failed'];
if (noRetryTypes.includes(error.type)) {
return false;
}
// 如果提供了自定义重试条件函数,使用它
if (typeof retryCondition === 'function') {
return retryCondition(error);
}
// 默认重试条件:非特定错误都重试
return true;
}
资源自动清理 (_cleanupExpiredLocks)
/**
* 清理过期锁和队列
* 设计特点:
* 1. 定期执行:每60秒自动清理一次
* 2. 双重清理:同时清理过期锁和队列项
* 3. 优雅终止:清理时发送取消信号
* 4. 统计记录:记录清理操作便于监控
*/
_cleanupExpiredLocks() {
const now = Date.now();
const maxAge = this._defaults.maxLockAge;
// 清理过期锁
for (const [name, lockItem] of this._lockMap.entries()) {
if (lockItem.locked && (now - lockItem.createdAt) > maxAge) {
console.warn(`清理过期锁【${name}】,已锁定${now - lockItem.createdAt}ms`);
const error = new Error('锁过期自动清理');
error.type = 'timeout';
error.code = 'LOCK_EXPIRED';
if (lockItem.abortController) {
lockItem.abortController.abort(error);
}
this._lockMap.delete(name);
}
}
// 清理过期队列项
for (const [name, queue] of this._queueMap.entries()) {
// ... 清理逻辑
}
}
📚 使用方法
基本安装
// 方式1:使用默认单例(无控制台警告)
import { asyncLock, releaseLock } from './asyncLock';
// 方式2:创建自定义实例
import { createLockManager } from './asyncLock';
const myLockManager = createLockManager({
timeout: 10000,
maxQueueSize: 10,
tipHandler: (msg) => console.warn(msg)
});
// 方式3:使用带控制台警告的单例
import { verboseLockManager } from './asyncLock';
基础配置选项
const options = {
// 必需:锁名称(用于标识任务类型)
name: 'submitForm',
// 必需:异步任务函数
asyncFn: async (signal) => {
// signal 是 AbortSignal,用于取消任务
if (signal.aborted) throw new Error('任务已取消');
return await fetch('/api/submit', { signal }).then(r => r.json());
},
// 可选:任务超时时间(毫秒)
timeout: 8000,
// 可选:重试次数(默认0)
retryCount: 2,
// 可选:基础重试延迟(毫秒)
baseRetryDelay: 1000,
// 可选:最大重试延迟(毫秒)
maxRetryDelay: 10000,
// 可选:自定义重试条件函数
retryCondition: (error) => {
// 只对网络错误重试
return error.message.includes('Network') || error.message.includes('timeout');
},
// 可选:重复执行时的提示信息
repeatTip: '操作中,请稍后...',
// 可选:重复执行时是否抛出错误(默认true)
throwRepeatError: true,
// 可选:是否启用队列(默认false)
enableQueue: true,
// 可选:队列最大长度(默认100)
maxQueueSize: 5,
// 可选:成功回调
onSuccess: (result) => {
console.log('任务成功:', result);
},
// 可选:失败回调
onFail: (error) => {
console.error('任务失败:', error.message);
},
// 可选:提示处理器(用于显示重复提示)
tipHandler: (message) => {
Toast.warning(message);
}
};
执行任务
// 使用默认单例
try {
const result = await asyncLock(options);
console.log('执行结果:', result);
} catch (error) {
if (error.code === 'LOCKED') {
// 重复执行被拒绝
console.warn('请勿重复操作');
} else if (error.code === 'QUEUE_FULL') {
// 队列已满
console.error('系统繁忙,请稍后重试');
} else {
// 其他错误
console.error('执行失败:', error);
}
}
// 使用自定义实例
try {
const result = await myLockManager.execute(options);
console.log('执行结果:', result);
} catch (error) {
// 错误处理
}
锁管理操作
import {
asyncLock,
releaseLock,
releaseAllLocks,
cancelLockTask,
getLockStatus,
getStats,
resetStats
} from './asyncLock';
// 1. 手动释放指定锁
releaseLock('submitForm');
// 2. 释放所有锁
releaseAllLocks();
// 3. 取消正在执行的任务
const cancelled = cancelLockTask('submitForm', '用户主动取消');
if (cancelled) {
console.log('任务已取消');
}
// 4. 获取锁状态
const status = getLockStatus('submitForm');
console.log('锁状态:', {
是否锁定: status.locked,
锁定时长: `${status.age}ms`,
队列长度: status.queueLength
});
// 5. 获取统计信息
const stats = getStats();
console.log('执行统计:', {
总执行次数: stats.totalExecutions,
成功次数: stats.successCount,
超时次数: stats.timeoutCount,
当前活跃锁: stats.activeLocks.length
});
// 6. 重置统计
resetStats();
📋 使用案例
案例1:表单提交防重复
/**
* 场景:表单提交按钮防止用户重复点击
* 需求:提交期间禁用按钮,防止重复提交,支持取消
*/
class FormSubmitService {
constructor() {
this.isSubmitting = false;
}
async submitForm(formData) {
if (this.isSubmitting) {
Toast.warning('正在提交,请稍候...');
return;
}
this.isSubmitting = true;
try {
const result = await asyncLock({
name: 'formSubmit',
asyncFn: async (signal) => {
// 模拟API调用
const response = await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
signal
});
if (!response.ok) {
throw new Error(`提交失败: ${response.status}`);
}
return await response.json();
},
timeout: 10000,
retryCount: 1,
baseRetryDelay: 2000,
repeatTip: '正在提交中,请勿重复点击',
tipHandler: (msg) => Toast.warning(msg),
onSuccess: (result) => {
Toast.success('提交成功!');
console.log('提交结果:', result);
},
onFail: (error) => {
if (error.code !== 'LOCKED') {
Toast.error(`提交失败: ${error.message}`);
}
}
});
return result;
} finally {
this.isSubmitting = false;
}
}
// 用户离开页面时取消提交
cancelSubmit() {
cancelLockTask('formSubmit', '用户离开页面');
}
}
// 使用
const formService = new FormSubmitService();
// 提交表单
submitButton.addEventListener('click', async () => {
const formData = collectFormData();
await formService.submitForm(formData);
});
// 页面离开时取消
window.addEventListener('beforeunload', () => {
formService.cancelSubmit();
});
案例2:支付订单防重复
/**
* 场景:支付订单防止重复支付
* 需求:支付期间锁定订单,防止重复支付,支持队列
*/
class PaymentService {
constructor(orderId) {
this.orderId = orderId;
this.lockName = `payment_${orderId}`;
}
async processPayment(paymentData) {
try {
return await asyncLock({
name: this.lockName,
asyncFn: async (signal) => {
// 调用支付接口
const paymentResult = await this.callPaymentApi(paymentData, signal);
// 更新订单状态
await this.updateOrderStatus(paymentResult, signal);
return paymentResult;
},
timeout: 30000, // 支付操作需要更长时间
retryCount: 2,
baseRetryDelay: 3000,
maxRetryDelay: 15000,
// 只对网络错误和服务器5xx错误重试
retryCondition: (error) => {
const isNetworkError = error.message.includes('Network') ||
error.message.includes('fetch');
const isServerError = error.message.includes('50') ||
error.message.includes('服务不可用');
return isNetworkError || isServerError;
},
enableQueue: true,
maxQueueSize: 1, // 同一订单只允许一个排队
repeatTip: '订单支付处理中,请稍候...',
tipHandler: (msg) => {
showPaymentStatus(msg);
},
onSuccess: (result) => {
showPaymentSuccess(result);
trackPaymentEvent('success', this.orderId);
},
onFail: (error) => {
if (error.code === 'LOCKED') {
// 重复支付被阻止
trackPaymentEvent('prevented_duplicate', this.orderId);
} else if (error.code === 'QUEUE_FULL') {
showPaymentError('订单正在处理,请勿重复操作');
} else {
showPaymentError(`支付失败: ${error.message}`);
trackPaymentEvent('failed', this.orderId, error);
}
}
});
} catch (error) {
console.error('支付处理异常:', error);
throw error;
}
}
async callPaymentApi(paymentData, signal) {
// 模拟支付API调用
const response = await fetch('/api/payment/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orderId: this.orderId,
...paymentData
}),
signal
});
if (!response.ok) {
throw new Error(`支付API错误: ${response.status}`);
}
return await response.json();
}
async updateOrderStatus(paymentResult, signal) {
// 更新订单状态
const response = await fetch(`/api/orders/${this.orderId}/status`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
status: 'paid',
paymentId: paymentResult.paymentId,
paidAt: new Date().toISOString()
}),
signal
});
if (!response.ok) {
throw new Error(`订单状态更新失败: ${response.status}`);
}
}
// 取消支付
cancelPayment() {
const cancelled = cancelLockTask(this.lockName, '用户取消支付');
if (cancelled) {
showPaymentStatus('支付已取消');
trackPaymentEvent('cancelled', this.orderId);
}
return cancelled;
}
}
// 使用
const paymentService = new PaymentService('ORDER_123456');
// 开始支付
paymentButton.addEventListener('click', async () => {
const paymentData = {
amount: 100.00,
method: 'credit_card',
cardToken: 'tok_123456'
};
try {
await paymentService.processPayment(paymentData);
} catch (error) {
console.error('支付失败:', error);
}
});
// 取消支付
cancelButton.addEventListener('click', () => {
paymentService.cancelPayment();
});
案例3:文件上传队列管理
/**
* 场景:批量文件上传,需要控制并发和防止重复上传
* 需求:同一文件不能重复上传,上传任务需要排队
*/
class FileUploadManager {
constructor() {
this.uploadQueue = new Map(); // fileId -> upload promise
}
async uploadFile(file, options = {}) {
const fileId = this.generateFileId(file);
const lockName = `upload_${fileId}`;
// 如果已经在队列中,返回已有的Promise
if (this.uploadQueue.has(fileId)) {
return this.uploadQueue.get(fileId);
}
const uploadPromise = asyncLock({
name: lockName,
asyncFn: async (signal) => {
try {
// 更新UI状态
this.updateFileStatus(fileId, 'uploading');
// 执行上传
const result = await this.doUpload(file, signal, options);
// 上传成功
this.updateFileStatus(fileId, 'success');
return result;
} catch (error) {
// 上传失败
this.updateFileStatus(fileId, 'error');
throw error;
}
},
timeout: 5 * 60 * 1000, // 5分钟超时
retryCount: 3,
baseRetryDelay: 5000,
maxRetryDelay: 60000,
retryCondition: (error) => {
// 只对网络错误重试
return error.message.includes('network') ||
error.message.includes('timeout') ||
error.message.includes('Network');
},
enableQueue: true,
maxQueueSize: 0, // 同一文件不上传队列
repeatTip: '文件正在上传中...',
tipHandler: (msg) => {
console.log(`文件 ${file.name}: ${msg}`);
},
onSuccess: (result) => {
console.log(`文件 ${file.name} 上传成功:`, result);
this.uploadQueue.delete(fileId);
},
onFail: (error) => {
console.error(`文件 ${file.name} 上传失败:`, error);
this.uploadQueue.delete(fileId);
},
autoCleanup: false // 手动清理,避免上传完成前锁被清理
});
// 保存到队列
this.uploadQueue.set(fileId, uploadPromise);
return uploadPromise;
}
async doUpload(file, signal, options) {
const formData = new FormData();
formData.append('file', file);
// 添加上传进度回调
const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => {
// 监听取消信号
if (signal.aborted) {
reject(new Error('上传被取消'));
return;
}
const onAbort = () => {
xhr.abort();
reject(new Error('上传被取消'));
};
signal.addEventListener('abort', onAbort);
// 设置上传进度
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
this.updateUploadProgress(fileId, percent);
}
});
// 完成处理
xhr.addEventListener('load', () => {
signal.removeEventListener('abort', onAbort);
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`上传失败: ${xhr.status} ${xhr.statusText}`));
}
});
xhr.addEventListener('error', () => {
signal.removeEventListener('abort', onAbort);
reject(new Error('网络错误,上传失败'));
});
xhr.addEventListener('abort', () => {
signal.removeEventListener('abort', onAbort);
reject(new Error('上传被取消'));
});
// 开始上传
xhr.open('POST', '/api/upload');
xhr.send(formData);
});
}
generateFileId(file) {
// 生成文件唯一ID(实际项目中可能需要更复杂的逻辑)
return `${file.name}_${file.size}_${file.lastModified}`;
}
updateFileStatus(fileId, status) {
// 更新UI显示
console.log(`文件 ${fileId} 状态: ${status}`);
}
updateUploadProgress(fileId, percent) {
// 更新上传进度
console.log(`文件 ${fileId} 上传进度: ${percent}%`);
}
// 取消文件上传
cancelUpload(file) {
const fileId = this.generateFileId(file);
const lockName = `upload_${fileId}`;
const cancelled = cancelLockTask(lockName, '用户取消上传');
if (cancelled) {
this.uploadQueue.delete(fileId);
this.updateFileStatus(fileId, 'cancelled');
console.log(`文件 ${file.name} 上传已取消`);
}
return cancelled;
}
// 批量取消所有上传
cancelAllUploads() {
releaseAllLocks();
this.uploadQueue.clear();
console.log('所有文件上传已取消');
}
}
// 使用
const uploadManager = new FileUploadManager();
// 上传文件
fileInput.addEventListener('change', async (event) => {
const files = Array.from(event.target.files);
for (const file of files) {
try {
await uploadManager.uploadFile(file);
} catch (error) {
console.error(`文件 ${file.name} 上传失败:`, error);
}
}
});
// 取消上传
cancelButton.addEventListener('click', () => {
const file = getSelectedFile();
uploadManager.cancelUpload(file);
});
案例4:全局配置管理
/**
* 场景:应用全局配置需要防止并发修改
* 需求:配置更新需要互斥,多个更新请求需要排队
*/
class ConfigManager {
constructor() {
this.config = {};
this.lockManager = createLockManager({
timeout: 15000,
maxLockAge: 2 * 60 * 1000, // 2分钟
maxQueueSize: 5,
tipHandler: (msg) => console.log('[ConfigLock]', msg),
enableStats: true
});
}
async updateConfig(key, value, options = {}) {
const lockName = `config_${key}`;
try {
const result = await this.lockManager.execute({
name: lockName,
asyncFn: async (signal) => {
// 获取当前配置
const currentConfig = await this.fetchConfig(key, signal);
// 验证配置
if (options.validate) {
const isValid = await options.validate(value, currentConfig, signal);
if (!isValid) {
throw new Error('配置验证失败');
}
}
// 更新配置
const updateResult = await this.doUpdateConfig(key, value, signal);
// 更新本地缓存
this.config[key] = value;
// 触发配置变更事件
this.emitConfigChange(key, value, currentConfig);
return updateResult;
},
timeout: options.timeout || 10000,
retryCount: options.retryCount || 1,
baseRetryDelay: 2000,
retryCondition: (error) => {
// 只对网络错误重试
return error.message.includes('network') ||
error.message.includes('timeout') ||
error.name === 'TypeError'; // fetch错误
},
enableQueue: true,
onSuccess: (result) => {
console.log(`配置 ${key} 更新成功:`, result);
},
onFail: (error) => {
if (error.code !== 'LOCKED') {
console.error(`配置 ${key} 更新失败:`, error);
}
}
});
return result;
} catch (error) {
console.error(`配置 ${key} 更新异常:`, error);
throw error;
}
}
async fetchConfig(key, signal) {
const response = await fetch(`/api/config/${key}`, { signal });
if (!response.ok) {
throw new Error(`获取配置失败: ${response.status}`);
}
return await response.json();
}
async doUpdateConfig(key, value, signal) {
const response = await fetch(`/api/config/${key}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ value }),
signal
});
if (!response.ok) {
throw new Error(`更新配置失败: ${response.status}`);
}
return await response.json();
}
emitConfigChange(key, newValue, oldValue) {
// 触发配置变更事件
const event = new CustomEvent('configChange', {
detail: { key, newValue, oldValue }
});
window.dispatchEvent(event);
}
// 批量更新配置(多个配置项原子更新)
async batchUpdateConfig(updates, options = {}) {
const lockName = 'config_batch_update';
return await this.lockManager.execute({
name: lockName,
asyncFn: async (signal) => {
// 开始事务
const transactionId = await this.beginTransaction(signal);
try {
const results = {};
// 依次更新每个配置
for (const [key, value] of Object.entries(updates)) {
const result = await this.doUpdateConfig(key, value, signal);
results[key] = result;
this.config[key] = value;
}
// 提交事务
await this.commitTransaction(transactionId, signal);
// 触发批量变更事件
this.emitBatchConfigChange(updates);
return results;
} catch (error) {
// 回滚事务
await this.rollbackTransaction(transactionId, signal);
throw error;
}
},
timeout: 30000, // 批量操作需要更长时间
retryCount: 0, // 批量操作不重试
enableQueue: true,
maxQueueSize: 1 // 批量操作只允许一个排队
});
}
async beginTransaction(signal) {
const response = await fetch('/api/config/transaction/begin', { signal });
if (!response.ok) {
throw new Error('开始事务失败');
}
const data = await response.json();
return data.transactionId;
}
async commitTransaction(transactionId, signal) {
const response = await fetch('/api/config/transaction/commit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transactionId }),
signal
});
if (!response.ok) {
throw new Error('提交事务失败');
}
}
async rollbackTransaction(transactionId, signal) {
await fetch('/api/config/transaction/rollback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transactionId }),
signal
}).catch(() => {
// 回滚失败也继续,不影响主流程
});
}
emitBatchConfigChange(updates) {
const event = new CustomEvent('configBatchChange', {
detail: { updates }
});
window.dispatchEvent(event);
}
// 获取锁管理器统计信息(用于监控)
getLockStats() {
return this.lockManager.getStats();
}
// 清理所有配置锁
cleanupConfigLocks() {
this.lockManager.releaseAllLocks();
}
}
// 使用
const configManager = new ConfigManager();
// 更新单个配置
async function updateTheme(theme) {
try {
await configManager.updateConfig('theme', theme, {
validate: async (value, current) => {
// 验证主题是否有效
const validThemes = ['light', 'dark', 'auto'];
return validThemes.includes(value);
},
retryCount: 2
});
} catch (error) {
if (error.code === 'LOCKED') {
console.log('配置正在更新中,请稍后');
} else {
console.error('更新主题失败:', error);
}
}
}
// 批量更新配置
async function updateUserPreferences(prefs) {
try {
const results = await configManager.batchUpdateConfig(prefs);
console.log('偏好设置更新成功:', results);
} catch (error) {
console.error('批量更新失败:', error);
}
}
// 监控锁状态
setInterval(() => {
const stats = configManager.getLockStats();
if (stats.activeLocks.length > 0) {
console.log('活跃的配置锁:', stats.activeLocks);
}
}, 60000);
🔧 高级配置
自定义重试策略
// 基于错误类型的智能重试策略
const smartRetryCondition = (error) => {
// 网络错误:重试
if (error.name === 'NetworkError' ||
error.name === 'TypeError' ||
error.message.includes('network')) {
return true;
}
// 服务器5xx错误:重试
if (error.status >= 500 && error.status < 600) {
return true;
}
// 服务器4xx错误(除429外):不重试
if (error.status >= 400 && error.status < 500 && error.status !== 429) {
return false;
}
// 429 Too Many Requests:使用退避重试
if (error.status === 429) {
return true;
}
// 默认情况:不重试
return false;
};
// 使用自定义重试条件
await asyncLock({
name: 'apiCall',
asyncFn: apiCallFunction,
retryCount: 3,
retryCondition: smartRetryCondition
});
性能监控集成
// 创建带监控的锁管理器
class MonitoredLockManager extends LockManager {
constructor(options = {}) {
super(options);
this.metrics = {
lockAcquisitionTime: [],
taskExecutionTime: [],
queueWaitTime: []
};
}
async execute(options) {
const startTime = performance.now();
try {
const result = await super.execute(options);
// 记录执行时间
const endTime = performance.now();
const executionTime = endTime - startTime;
this.metrics.taskExecutionTime.push(executionTime);
// 发送性能指标
this.sendMetrics({
name: options.name,
executionTime,
success: true
});
return result;
} catch (error) {
const endTime = performance.now();
const executionTime = endTime - startTime;
// 发送错误指标
this.sendMetrics({
name: options.name,
executionTime,
success: false,
errorType: error.type,
errorCode: error.code
});
throw error;
}
}
sendMetrics(metric) {
// 发送到监控系统
console.log('[LockMetrics]', metric);
// 实际项目中可以发送到 APM 系统
// sendToAPM('lock_execution', metric);
}
getPerformanceMetrics() {
const calculateStats = (array) => {
if (array.length === 0) return null;
const sum = array.reduce((a, b) => a + b, 0);
const avg = sum / array.length;
const max = Math.max(...array);
const min = Math.min(...array);
return { count: array.length, avg, min, max, sum };
};
return {
taskExecution: calculateStats(this.metrics.taskExecutionTime),
lockAcquisition: calculateStats(this.metrics.lockAcquisitionTime),
queueWait: calculateStats(this.metrics.queueWaitTime)
};
}
}
// 使用带监控的锁管理器
const monitoredManager = new MonitoredLockManager();
// 定期打印性能指标
setInterval(() => {
const metrics = monitoredManager.getPerformanceMetrics();
console.log('锁管理器性能指标:', metrics);
}, 60000);
📊 性能建议
最佳实践
- 合理设置超时时间
-
- 快速操作:1-5秒
- 普通操作:5-10秒
- 长时间操作:10-30秒
- 文件上传等:1-5分钟
- 队列配置建议
-
- 关键操作:队列大小 1(确保严格顺序)
- 普通操作:队列大小 3-5
- 批量操作:队列大小 10-20
- 注意:队列越大,内存占用越高
- 重试策略建议
-
- 网络请求:重试2-3次,基础延迟1-3秒
- 支付操作:重试1-2次,基础延迟2-5秒
- 文件操作:重试0-1次,基础延迟5-10秒
- 内存管理
-
- 定期检查锁状态,避免内存泄漏
- 页面卸载时调用
destroy() 清理资源
- 监控队列长度,避免无限增长
- 错误处理
-
- 区分用户取消和系统错误
- 对不同的错误类型采取不同的处理策略
- 记录详细的错误日志以便排查
🐛 常见问题
Q1: 锁会自动释放吗?
A: 是的。锁会在以下情况下自动释放:
- 任务执行完成(成功或失败)
- 任务超时
- 锁过期(超过
maxLockAge 配置)
- 手动调用
releaseLock() 或 releaseAllLocks()
Q2: 队列中的任务会按顺序执行吗?
A: 是的。队列采用 FIFO(先进先出)原则,任务会按照加入队列的顺序依次执行。
Q3: 如何防止内存泄漏?
A: 锁管理器内置以下防护措施:
- 定期清理过期锁(默认60秒一次)
- 队列项超时自动清理
- 页面卸载时可以调用
destroy() 方法
- 所有定时器和事件监听器都有清理逻辑
Q4: 支持分布式环境吗?
A: 当前版本是单机内存锁,适用于单页面应用或单服务器环境。如果需要分布式锁,可以基于此模式扩展,使用 Redis 或其他分布式存储作为锁存储后端。
Q5: 如何监控锁管理器的状态?
A: 可以通过以下方式监控:
- 使用
getLockStatus(name) 获取特定锁状态
- 使用
getStats() 获取全局统计信息
- 继承
LockManager 类添加自定义监控
- 监听相关事件(需要自行扩展事件系统)
📈 扩展建议
如果未来需要扩展功能,可以考虑:
-
分布式锁支持:集成 Redis 或其他分布式存储
-
锁优先级:为队列中的任务添加优先级
-
锁续期机制:长时间任务自动续期
-
事件系统:锁状态变化时触发事件
-
浏览器存储持久化:页面刷新后恢复锁状态
-
更复杂的队列算法:支持优先级队列、延迟队列等
📝 总结
LockManager 是一个功能全面、设计优雅的异步任务互斥锁工具,它解决了传统防抖节流方案的诸多痛点,特别适合以下场景:
- ✅ 表单提交:防止重复提交
- ✅ 支付操作:防止重复支付
- ✅ 文件上传:同一文件不上传多次
- ✅ 配置更新:防止并发修改配置
- ✅ 关键操作:需要严格顺序执行的操作
- ✅ 资源竞争:多组件共享资源时的并发控制
通过合理使用这个工具,可以显著提升应用的数据一致性和用户体验,避免因并发操作导致的业务逻辑错误。
📄 完整代码
-
默认单例 (
asyncLock):适合大多数场景
-
自定义实例 (
createLockManager):需要不同配置时使用
-
类直接使用 (
LockManager):需要继承扩展时使用
工具已经过精心设计和测试,可以直接在生产环境中使用。
/**
* 异步任务互斥锁工具
* 需求:防抖节流不能防止api接口长时间未返回。如果用户等待一小段时候后重新点击提交,会导致重新触发请求;
* 解决思路:用互斥锁思路处理异步任务锁定,通过name进行异步任务锁定,防止重入。
* 核心能力:防止异步任务未完成时重复执行、超时控制、任务取消、资源自动清理
* 支持:队列机制、指数退避重试、原子操作、错误分类、性能监控
*/
class LockManager {
constructor(options = {}) {
// 存储所有锁状态
this._lockMap = new Map();
// 等待队列
this._queueMap = new Map();
// 默认配置
this._defaults = {
timeout: 10000,
repeatTip: '操作中,请稍后...',
throwRepeatError: true,
autoCleanup: true,
maxLockAge: 5 * 60 * 1000,
maxQueueSize: 100,
enableStats: true,
tipHandler: () => {},
...options
};
// 统计信息
this._stats = {
totalExecutions: 0,
successCount: 0,
timeoutCount: 0,
cancelCount: 0,
repeatRejectCount: 0,
queueFullCount: 0,
retryCount: 0
};
// 定期清理过期锁和队列
this._cleanupInterval = setInterval(() => this._cleanupExpiredLocks(), 60000);
// 绑定方法,确保在回调中使用正确的this
this._processNextInQueue = this._processNextInQueue.bind(this);
}
/**
* 原子性地获取锁
*/
_acquireLock(name) {
const attemptId = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
const now = Date.now();
// 第一重检查
const existing = this._lockMap.get(name);
if (existing?.locked) {
return null;
}
// 创建新的锁对象
const lockItem = {
locked: true,
abortController: new AbortController(),
timeoutTimer: null,
createdAt: now,
taskId: `${name}_${now}_${Math.random().toString(36).slice(2, 10)}`,
attemptId: attemptId,
waitingQueue: this._queueMap.get(name) || []
};
// 第二重检查(原子性保障)
const current = this._lockMap.get(name);
if (current?.locked) {
return null;
}
// 设置锁(原子操作)
this._lockMap.set(name, lockItem);
// 最终验证
const afterSet = this._lockMap.get(name);
if (afterSet?.attemptId !== attemptId) {
lockItem.abortController.abort();
return null;
}
return lockItem;
}
/**
* 将任务加入等待队列
*/
_addToQueue(options) {
const { name, maxQueueSize = this._defaults.maxQueueSize } = options;
let queue = this._queueMap.get(name);
if (!queue) {
queue = [];
this._queueMap.set(name, queue);
}
if (queue.length >= maxQueueSize) {
this._stats.queueFullCount++;
const error = new Error(`任务队列【${name}】已满(最大${maxQueueSize})`);
error.type = 'queue_full';
error.code = 'QUEUE_FULL';
return Promise.reject(error);
}
return new Promise((resolve, reject) => {
const queueItem = {
options,
resolve,
reject,
enqueuedAt: Date.now()
};
queue.push(queueItem);
if (options.timeout > 0) {
queueItem.timeoutTimer = setTimeout(() => {
const index = queue.indexOf(queueItem);
if (index > -1) {
queue.splice(index, 1);
const error = new Error(`任务【${name}】在队列中等待超时`);
error.type = 'queue_timeout';
error.code = 'QUEUE_TIMEOUT';
reject(error);
if (queue.length === 0) {
this._queueMap.delete(name);
}
}
}, options.timeout);
}
});
}
/**
* 处理队列中的下一个任务(使用微任务)
*/
async _processNextInQueue(name) {
const queue = this._queueMap.get(name);
if (!queue || queue.length === 0) {
this._queueMap.delete(name);
return;
}
// 使用微任务处理,避免 setTimeout 的延迟
await Promise.resolve();
const queueItem = queue.shift();
if (queueItem.timeoutTimer) {
clearTimeout(queueItem.timeoutTimer);
}
try {
const result = await this._executeTask(queueItem.options);
queueItem.resolve(result);
} catch (error) {
queueItem.reject(error);
} finally {
// 继续处理下一个(递归)
if (queue.length > 0) {
// 再次使用微任务
Promise.resolve().then(() => this._processNextInQueue(name));
} else {
this._queueMap.delete(name);
}
}
}
/**
* 执行任务核心逻辑
*/
async _executeTask(options) {
const {
name,
asyncFn,
timeout = this._defaults.timeout,
retryCount = 0,
baseRetryDelay = 1000,
maxRetryDelay = 30000,
retryCondition = null // 自定义重试条件函数
} = options;
const lockItem = this._acquireLock(name);
if (!lockItem) {
const error = new Error(`无法获取锁【${name}】`);
error.type = 'lock_failed';
error.code = 'LOCK_FAILED';
throw error;
}
let result;
try {
if (timeout > 0) {
lockItem.timeoutTimer = setTimeout(() => {
const timeoutError = new Error(`任务【${name}】超时(${timeout}ms)`);
timeoutError.type = 'timeout';
timeoutError.code = 'TIMEOUT';
lockItem.abortController.abort(timeoutError);
}, timeout);
}
result = await this._executeWithExponentialBackoff(
() => asyncFn(lockItem.abortController.signal),
retryCount,
baseRetryDelay,
maxRetryDelay,
lockItem.abortController,
retryCondition // 传递重试条件
);
return result;
} catch (error) {
error.lockName = name;
error.taskId = lockItem.taskId;
throw error;
} finally {
this._cleanupLock(name, lockItem, options.autoCleanup ?? this._defaults.autoCleanup);
// 使用微任务处理下一个队列任务
Promise.resolve().then(() => this._processNextInQueue(name));
}
}
/**
* 判断是否应该重试(支持自定义重试条件)
*/
_shouldRetry(error, retryCondition) {
// 不重试的错误类型
const noRetryTypes = ['cancel', 'timeout', 'queue_full', 'queue_timeout', 'lock_failed'];
if (noRetryTypes.includes(error.type)) {
return false;
}
// 如果提供了自定义重试条件函数,使用它
if (typeof retryCondition === 'function') {
return retryCondition(error);
}
// 默认重试条件:非特定错误都重试
return true;
}
/**
* 指数退避重试执行(支持自定义重试条件)
*/
async _executeWithExponentialBackoff(fn, maxRetries, baseDelay, maxDelay, abortController, retryCondition) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
if (abortController.signal.aborted) {
const cancelError = new Error('任务已被取消');
cancelError.type = 'cancel';
cancelError.code = 'CANCELLED';
throw cancelError;
}
if (attempt > 0) {
const delay = this._calculateExponentialBackoffDelay(
attempt,
baseDelay,
maxDelay
);
this._stats.retryCount++;
console.log(`任务重试第${attempt}次,延迟${delay}ms`);
await this._sleep(delay, abortController.signal);
}
return await fn();
} catch (error) {
lastError = error;
// 使用统一的判断逻辑决定是否重试
if (!this._shouldRetry(error, retryCondition)) {
throw error;
}
if (attempt === maxRetries) {
error.retryAttempts = attempt;
throw error;
}
}
}
throw lastError;
}
/**
* 计算指数退避延迟
*/
_calculateExponentialBackoffDelay(attempt, baseDelay, maxDelay) {
const exponentialDelay = baseDelay * Math.pow(2, attempt - 1);
const jitter = exponentialDelay * 0.1 * Math.random();
return Math.min(exponentialDelay + jitter, maxDelay);
}
/**
* 可中断的延时(安全的事件监听清理)
*/
_sleep(ms, signal) {
return new Promise((resolve, reject) => {
if (signal.aborted) {
reject(new Error('等待被中断'));
return;
}
const timer = setTimeout(() => {
// 清理事件监听
signal.removeEventListener('abort', abortHandler);
resolve();
}, ms);
const abortHandler = () => {
clearTimeout(timer);
const error = new Error('等待被中断');
error.type = 'cancel';
error.code = 'SLEEP_CANCELLED';
reject(error);
};
signal.addEventListener('abort', abortHandler);
// 确保在Promise settled后清理
const cleanup = () => {
clearTimeout(timer);
signal.removeEventListener('abort', abortHandler);
};
// 无论成功还是失败都执行清理
this._safeFinally(() => {
cleanup();
}, resolve, reject);
});
}
/**
* 安全的finally执行,避免影响原始Promise
*/
_safeFinally(cleanupFn, resolve, reject) {
const wrappedResolve = (value) => {
try {
cleanupFn();
} finally {
resolve(value);
}
};
const wrappedReject = (error) => {
try {
cleanupFn();
} finally {
reject(error);
}
};
return { resolve: wrappedResolve, reject: wrappedReject };
}
/**
* 清理锁资源
*/
_cleanupLock(name, lockItem, autoCleanup) {
if (lockItem.timeoutTimer) {
clearTimeout(lockItem.timeoutTimer);
lockItem.timeoutTimer = null;
}
if (lockItem.abortController) {
lockItem.abortController = null;
}
if (autoCleanup) {
this._lockMap.delete(name);
} else {
lockItem.locked = false;
lockItem.abortController = null;
lockItem.timeoutTimer = null;
}
}
/**
* 清理过期锁和队列
*/
_cleanupExpiredLocks() {
const now = Date.now();
const maxAge = this._defaults.maxLockAge;
// 清理过期锁
for (const [name, lockItem] of this._lockMap.entries()) {
if (lockItem.locked && (now - lockItem.createdAt) > maxAge) {
console.warn(`清理过期锁【${name}】,已锁定${now - lockItem.createdAt}ms`);
const error = new Error('锁过期自动清理');
error.type = 'timeout';
error.code = 'LOCK_EXPIRED';
if (lockItem.abortController) {
lockItem.abortController.abort(error);
}
this._lockMap.delete(name);
}
}
// 清理过期队列项
for (const [name, queue] of this._queueMap.entries()) {
const expiredItems = [];
for (let i = 0; i < queue.length; i++) {
const item = queue[i];
const queueAge = now - item.enqueuedAt;
const timeout = item.options?.timeout || 30000;
if (queueAge > timeout) {
expiredItems.push(i);
}
}
for (let i = expiredItems.length - 1; i >= 0; i--) {
const index = expiredItems[i];
const item = queue[index];
if (item.timeoutTimer) {
clearTimeout(item.timeoutTimer);
}
const error = new Error(`任务【${name}】在队列中过期`);
error.type = 'queue_timeout';
error.code = 'QUEUE_TIMEOUT';
item.reject(error);
queue.splice(index, 1);
}
if (queue.length === 0) {
this._queueMap.delete(name);
}
}
}
/**
* 执行带锁的异步任务
*/
async execute(options) {
const {
name,
asyncFn,
onSuccess,
onFail,
repeatTip = this._defaults.repeatTip,
timeout = this._defaults.timeout,
throwRepeatError = this._defaults.throwRepeatError,
tipHandler = this._defaults.tipHandler, // 使用配置的默认值
enableQueue = false,
maxQueueSize = this._defaults.maxQueueSize,
retryCount = 0,
baseRetryDelay = 1000,
maxRetryDelay = 30000,
retryCondition = null, // 自定义重试条件
autoCleanup = this._defaults.autoCleanup
} = options;
this._stats.totalExecutions++;
try {
const existingLock = this._lockMap.get(name);
if (existingLock?.locked) {
this._stats.repeatRejectCount++;
const repeatError = new Error(repeatTip);
repeatError.type = 'repeat';
repeatError.code = 'LOCKED';
repeatError.lockName = name;
tipHandler(repeatTip);
if (enableQueue) {
console.log(`任务【${name}】加入等待队列,当前队列长度:${this._queueMap.get(name)?.length || 0}`);
const queueOptions = {
...options,
enableQueue: false,
maxQueueSize: undefined
};
const queueResult = await this._addToQueue({
...queueOptions,
name,
maxQueueSize
});
onSuccess?.(queueResult);
return queueResult;
} else {
onFail?.(repeatError);
if (throwRepeatError) throw repeatError;
return Promise.reject(repeatError);
}
}
const result = await this._executeTask({
name,
asyncFn,
timeout,
retryCount,
baseRetryDelay,
maxRetryDelay,
retryCondition, // 传递重试条件
autoCleanup
});
this._stats.successCount++;
onSuccess?.(result);
return result;
} catch (error) {
switch (error.type) {
case 'timeout':
this._stats.timeoutCount++;
break;
case 'cancel':
this._stats.cancelCount++;
break;
case 'queue_full':
this._stats.queueFullCount++;
break;
}
onFail?.(error);
throw error;
}
}
/**
* 手动释放指定锁
*/
releaseLock(name) {
const lockItem = this._lockMap.get(name);
if (lockItem) {
this._cleanupLock(name, lockItem, true);
}
const queue = this._queueMap.get(name);
if (queue) {
queue.forEach(item => {
if (item.timeoutTimer) {
clearTimeout(item.timeoutTimer);
}
const error = new Error('锁被手动释放,队列任务取消');
error.type = 'cancel';
error.code = 'MANUAL_RELEASE';
item.reject(error);
});
this._queueMap.delete(name);
}
}
/**
* 批量释放所有锁
*/
releaseAllLocks() {
this._lockMap.forEach((lockItem, name) => {
this._cleanupLock(name, lockItem, true);
});
this._lockMap.clear();
this._queueMap.forEach((queue, name) => {
queue.forEach(item => {
if (item.timeoutTimer) {
clearTimeout(item.timeoutTimer);
}
const error = new Error('所有锁被释放,队列任务取消');
error.type = 'cancel';
error.code = 'ALL_RELEASED';
item.reject(error);
});
});
this._queueMap.clear();
}
/**
* 取消正在执行的任务
*/
cancelLockTask(name, reason = "用户主动取消") {
const lockItem = this._lockMap.get(name);
if (lockItem?.locked && lockItem.abortController) {
const error = new Error(reason);
error.type = 'cancel';
error.code = 'USER_CANCEL';
lockItem.abortController.abort(error);
this._cleanupLock(name, lockItem, true);
return true;
}
return false;
}
/**
* 获取指定任务的锁状态
*/
getLockStatus(name) {
const lockItem = this._lockMap.get(name);
const queue = this._queueMap.get(name);
return {
locked: lockItem?.locked ?? false,
taskId: lockItem?.taskId,
createdAt: lockItem?.createdAt,
age: lockItem ? Date.now() - lockItem.createdAt : 0,
hasAbortController: !!lockItem?.abortController,
queueLength: queue?.length || 0,
queueWaitTimes: queue?.map(item => Date.now() - item.enqueuedAt) || []
};
}
/**
* 获取统计信息
*/
getStats() {
return {
...this._stats,
activeLocks: Array.from(this._lockMap.entries())
.filter(([_, lock]) => lock.locked)
.map(([name, lock]) => ({
name,
age: Date.now() - lock.createdAt,
taskId: lock.taskId
})),
waitingQueues: Array.from(this._queueMap.entries())
.map(([name, queue]) => ({
name,
length: queue.length,
oldestWait: queue.length > 0 ? Date.now() - queue[0].enqueuedAt : 0
}))
};
}
/**
* 重置统计信息
*/
resetStats() {
this._stats = {
totalExecutions: 0,
successCount: 0,
timeoutCount: 0,
cancelCount: 0,
repeatRejectCount: 0,
queueFullCount: 0,
retryCount: 0
};
}
/**
* 销毁实例
*/
destroy() {
clearInterval(this._cleanupInterval);
this.releaseAllLocks();
this._queueMap.clear();
this._lockMap.clear();
}
}
// 创建锁管理器的工厂函数
export const createLockManager = (options) => new LockManager(options);
// 默认单例(无默认控制台警告)
export const defaultLockManager = new LockManager({
tipHandler: () => {} // 明确指定空函数
});
// 带控制台警告的单例(如果需要)
export const verboseLockManager = new LockManager({
tipHandler: console.warn
});
// 核心方法导出(使用默认单例)
export const asyncLock = (options) => defaultLockManager.execute(options);
export const releaseLock = (name) => defaultLockManager.releaseLock(name);
export const releaseAllLocks = () => defaultLockManager.releaseAllLocks();
export const cancelLockTask = (name, reason) => defaultLockManager.cancelLockTask(name, reason);
export const getLockStatus = (name) => defaultLockManager.getLockStatus(name);
export const getStats = () => defaultLockManager.getStats();
export const resetStats = () => defaultLockManager.resetStats();
export const destroyLockManager = () => defaultLockManager.destroy();
// 导出类本身
export { LockManager };
/*********************************************************************
* 使用示例
*********************************************************************/
/*
// 示例1:基础使用(无控制台警告)
import { asyncLock } from './asyncLock';
const submitForm = async () => {
try {
const result = await asyncLock({
name: 'formSubmit',
asyncFn: async (signal) => {
if (signal.aborted) throw new Error('任务已被取消');
return await api.submit(data);
},
timeout: 8000,
retryCount: 2,
baseRetryDelay: 1000,
maxRetryDelay: 10000,
onSuccess: (res) => console.log('提交成功:', res),
tipHandler: (msg) => console.warn(msg) // 需要时才传入
});
} catch (err) {
console.error('捕获到错误:', err);
}
};
// 示例2:自定义重试条件
const fetchWithRetry = async () => {
try {
const result = await asyncLock({
name: 'fetchData',
asyncFn: async (signal) => {
const response = await fetch('/api/data', { signal });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
},
retryCount: 3,
retryCondition: (error) => {
// 只对网络错误和5xx错误重试
return error.message.includes('Failed to fetch') ||
error.message.includes('HTTP 5');
},
onFail: (error) => {
if (!error.message.includes('HTTP 4')) {
console.error('需要重试的错误:', error);
}
}
});
} catch (err) {
console.error('最终失败:', err);
}
};
// 示例3:队列处理
const processWithQueue = async () => {
try {
const result = await asyncLock({
name: 'heavyProcess',
asyncFn: async (signal) => {
// 耗时处理
return await heavyProcessing();
},
enableQueue: true,
maxQueueSize: 10,
timeout: 30000,
onSuccess: (res) => {
console.log('处理完成,结果:', res);
}
});
} catch (err) {
if (err.code === 'QUEUE_FULL') {
alert('系统繁忙,请稍后重试');
}
}
};
// 示例4:使用verbose版本(需要控制台警告)
import { verboseLockManager } from './asyncLock';
const verboseTask = async () => {
const result = await verboseLockManager.execute({
name: 'verboseTask',
asyncFn: async () => {
// 任务逻辑
},
// 会自动输出控制台警告
});
};
// 示例5:多个锁管理器实例(隔离环境)
import { createLockManager } from './asyncLock';
const userLockManager = createLockManager({
maxQueueSize: 5,
tipHandler: (msg) => Toast.warning(msg)
});
const systemLockManager = createLockManager({
timeout: 30000,
tipHandler: console.error
});
// 分别使用
const userTask = async () => {
await userLockManager.execute({
name: 'userAction',
asyncFn: userAction
});
};
const systemTask = async () => {
await systemLockManager.execute({
name: 'systemTask',
asyncFn: systemTask
});
};
*/