javascript fetch api 本身不支持原生的 cas(compare-and-swap)策略,必须通过手动实现乐观锁机制来完成。具体做法是在请求头中携带当前资源的版本号(如 etag),并在服务端返回状态码为 412 precondition failed 时,根据业务逻辑决定是重试、报错还是合并数据。

如何构建带版本控制的请求与响应处理
在发起请求时,需要读取本地存储的当前资源版本,并将其放入 HTTP 请求头的 If-Match 字段中。服务端在处理更新请求时,会比对客户端提供的版本与服务端当前版本是否一致,若不一致则拒绝修改并返回 412 状态码。

const updateResource = async (url, data, currentVersion) => {
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'If-Match': currentVersion // 关键:携带当前版本
},
body: JSON.stringify(data)
});
if (response.status === 412) {
throw new Error('Conflict: Version mismatch');
}
if (!response.ok) {
throw new Error('Network response was not ok');
}
// 获取新版本的 ETag
const newVersion = response.headers.get('ETag');
return { success: true, version: newVersion };
};上述代码展示了基本的 CAS 流程。调用 updateResource 函数时,传入当前的 currentVersion。如果服务端发现版本不匹配,会抛出错误。开发者捕获该错误后,可以重新拉取最新数据并再次尝试提交。
立即学习“Java免费学习笔记(深入)”;
如何处理并发冲突与重试逻辑
当检测到 412 冲突时,通常需要进行重试。重试前必须先获取最新的资源数据和版本信息,否则直接重试会导致同样的冲突。这里需要实现一个循环或递归逻辑来自动处理冲突。

const optimisticUpdate = async (url, initialData, maxRetries = 3) => {
let currentVersion = localStorage.getItem('resource_version');
let currentData = JSON.parse(localStorage.getItem('resource_data'));
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const result = await updateResource(url, currentData, currentVersion);
// 更新本地状态
localStorage.setItem('resource_version', result.version);
localStorage.setItem('resource_data', JSON.stringify(currentData));
console.log('Update successful with version:', result.version);
return result;
} catch (error) {
if (error.message.includes('Conflict') && attempt < maxRetries) {
console.warn('Version conflict detected. Refetching...');
// 冲突时重新拉取最新数据
const refetchResponse = await fetch(url);
const newData = await refetchResponse.json();
currentVersion = refetchResponse.headers.get('ETag');
currentData = newData;
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
};这个 optimisticUpdate 函数封装了重试逻辑。它从本地存储获取当前版本和数据,然后尝试更新。如果发生冲突,它会重新拉取最新数据,更新本地状态,然后继续下一次尝试。这确保了最终一致性,但需要注意避免无限循环,因此设置了最大重试次数。
基于 If-Match 头部缺失时的行为问题
如果在请求中未设置 If-Match 头部,服务端的 CAS 机制将失效。此时,无论资源版本如何变化,请求都会正常执行,导致潜在的覆盖风险。这是实现 CAS 策略时必须检查的关键点。


















